package org.sasanlabs.service.vulnerability.jwt;

import static org.sasanlabs.service.vulnerability.jwt.bean.JWTUtils.GENERIC_BASE64_ENCODED_PAYLOAD;

import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.KeyUse;
import com.nimbusds.jose.jwk.RSAKey;
import java.io.UnsupportedEncodingException;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import org.sasanlabs.internal.utility.LevelConstants;
import org.sasanlabs.internal.utility.annotations.AttackVector;
import org.sasanlabs.internal.utility.annotations.VulnerableAppRequestMapping;
import org.sasanlabs.internal.utility.annotations.VulnerableAppRestController;
import org.sasanlabs.service.exception.ServiceApplicationException;
import org.sasanlabs.service.vulnerability.bean.GenericVulnerabilityResponseBean;
import org.sasanlabs.service.vulnerability.jwt.bean.JWTUtils;
import org.sasanlabs.service.vulnerability.jwt.keys.JWTAlgorithmKMS;
import org.sasanlabs.service.vulnerability.jwt.keys.KeyStrength;
import org.sasanlabs.service.vulnerability.jwt.keys.SymmetricAlgorithmKey;
import org.sasanlabs.vulnerability.types.VulnerabilityType;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * JWT client and server side implementation issues and remediations. Server side issues like: 1.
 * Weak HMAC key 2. none algorithm attack 3. Weak Hash algorithm 4. tweak Algorithm and Key.
 *
 * <p>Client side issues like: 1. Storing jwt in local storage/session storage hence if attacked
 * with XSS can be quite dangerous. 2. Storing jwt in cookies without httponly/secure flags or
 * cookie prefixes.
 *
 * <p>{@link https://github.com/SasanLabs/JWTExtension/blob/master/BrainStorming.md}
 *
 * @author KSASAN preetkaran20@gmail.com
 */
@VulnerableAppRestController(
        descriptionLabel = "JWT_INJECTION_VULNERABILITY",
        value = "JWTVulnerability")
public class JWTVulnerability {

    private IJWTTokenGenerator libBasedJWTGenerator;
    private IJWTValidator jwtValidator;

    private static final transient Logger LOGGER = LogManager.getLogger(JWTVulnerability.class);

    private static final String JWT = "JWT";
    private static final String JWT_COOKIE_KEY = JWT + "=";

    public JWTVulnerability(IJWTTokenGenerator libBasedJWTGenerator, IJWTValidator jwtValidator) {
        this.libBasedJWTGenerator = libBasedJWTGenerator;
        this.jwtValidator = jwtValidator;
    }

    private ResponseEntity<GenericVulnerabilityResponseBean<String>> getJWTResponseBean(
            boolean isValid,
            String jwtToken,
            boolean includeToken,
            MultiValueMap<String, String> headers) {
        GenericVulnerabilityResponseBean<String> genericVulnerabilityResponseBean;
        if (includeToken) {
            genericVulnerabilityResponseBean =
                    new GenericVulnerabilityResponseBean<String>(jwtToken, isValid);
        } else {
            genericVulnerabilityResponseBean =
                    new GenericVulnerabilityResponseBean<String>(null, isValid);
        }
        if (!isValid) {
            ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                    new ResponseEntity<GenericVulnerabilityResponseBean<String>>(
                            genericVulnerabilityResponseBean, headers, HttpStatus.UNAUTHORIZED);
            return responseEntity;
        }
        return new ResponseEntity<GenericVulnerabilityResponseBean<String>>(
                genericVulnerabilityResponseBean, headers, HttpStatus.OK);
    }

    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.CLIENT_SIDE_VULNERABLE_JWT,
            description = "JWT_URL_EXPOSING_SECURE_INFORMATION")
    @VulnerableAppRequestMapping(
            value = LevelConstants.LEVEL_1,
            htmlTemplate = "LEVEL_1/JWT_Level1")
    public ResponseEntity<GenericVulnerabilityResponseBean<String>>
            getVulnerablePayloadLevelUnsecure(@RequestParam Map<String, String> queryParams)
                    throws UnsupportedEncodingException, ServiceApplicationException {
        JWTAlgorithmKMS jwtAlgorithmKMS = new JWTAlgorithmKMS();
        Optional<SymmetricAlgorithmKey> symmetricAlgorithmKey =
                jwtAlgorithmKMS.getSymmetricAlgorithmKey(
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.HIGH);
        LOGGER.info(symmetricAlgorithmKey.isPresent() + " " + symmetricAlgorithmKey.get());
        String token = queryParams.get(JWT);
        if (token != null) {
            boolean isValid =
                    jwtValidator.customHMACValidator(
                            token,
                            JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                            JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
            return this.getJWTResponseBean(isValid, token, !isValid, null);
        } else {
            token =
                    libBasedJWTGenerator.getHMACSignedJWTToken(
                            JWTUtils.HS256_TOKEN_TO_BE_SIGNED,
                            JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                            JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
            return this.getJWTResponseBean(true, token, true, null);
        }
    }

    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.CLIENT_SIDE_VULNERABLE_JWT,
            description = "COOKIE_CONTAINING_JWT_TOKEN_SECURITY_ATTRIBUTES_MISSING")
    @VulnerableAppRequestMapping(
            value = LevelConstants.LEVEL_2,
            htmlTemplate = "LEVEL_2/JWT_Level2")
    public ResponseEntity<GenericVulnerabilityResponseBean<String>>
            getVulnerablePayloadLevelUnsecure2CookieBased(
                    RequestEntity<String> requestEntity,
                    @RequestParam Map<String, String> queryParams)
                    throws UnsupportedEncodingException, ServiceApplicationException {
        JWTAlgorithmKMS jwtAlgorithmKMS = new JWTAlgorithmKMS();
        Optional<SymmetricAlgorithmKey> symmetricAlgorithmKey =
                jwtAlgorithmKMS.getSymmetricAlgorithmKey(
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.HIGH);
        LOGGER.info(symmetricAlgorithmKey.isPresent() + " " + symmetricAlgorithmKey.get());
        List<String> tokens = requestEntity.getHeaders().get("cookie");
        boolean isFetch = Boolean.valueOf(queryParams.get("fetch"));
        if (!isFetch) {
            for (String token : tokens) {
                String[] cookieKeyValue = token.split(JWTUtils.BASE64_PADDING_CHARACTER_REGEX);
                if (cookieKeyValue[0].equals(JWT)) {
                    boolean isValid =
                            jwtValidator.customHMACValidator(
                                    cookieKeyValue[1],
                                    JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                                    JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
                    Map<String, List<String>> headers = new HashMap<>();
                    headers.put("Set-Cookie", Arrays.asList(token));
                    ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                            this.getJWTResponseBean(
                                    isValid,
                                    token,
                                    !isValid,
                                    CollectionUtils.toMultiValueMap(headers));
                    return responseEntity;
                }
            }
        }

        String token =
                libBasedJWTGenerator.getHMACSignedJWTToken(
                        JWTUtils.HS256_TOKEN_TO_BE_SIGNED,
                        JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
        Map<String, List<String>> headers = new HashMap<>();
        headers.put("Set-Cookie", Arrays.asList(JWT_COOKIE_KEY + token));
        ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                this.getJWTResponseBean(
                        true, token, true, CollectionUtils.toMultiValueMap(headers));
        return responseEntity;
    }

    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.CLIENT_SIDE_VULNERABLE_JWT,
            description = "COOKIE_WITH_HTTPONLY_WITHOUT_SECURE_FLAG_BASED_JWT_VULNERABILITY")
    @VulnerableAppRequestMapping(
            value = LevelConstants.LEVEL_3,
            htmlTemplate = "LEVEL_2/JWT_Level2")
    public ResponseEntity<GenericVulnerabilityResponseBean<String>>
            getVulnerablePayloadLevelUnsecure3CookieBased(
                    RequestEntity<String> requestEntity,
                    @RequestParam Map<String, String> queryParams)
                    throws UnsupportedEncodingException, ServiceApplicationException {
        JWTAlgorithmKMS jwtAlgorithmKMS = new JWTAlgorithmKMS();
        Optional<SymmetricAlgorithmKey> symmetricAlgorithmKey =
                jwtAlgorithmKMS.getSymmetricAlgorithmKey(
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.HIGH);
        LOGGER.info(symmetricAlgorithmKey.isPresent() + " " + symmetricAlgorithmKey.get());
        List<String> tokens = requestEntity.getHeaders().get("cookie");
        boolean isFetch = Boolean.valueOf(queryParams.get("fetch"));
        if (!isFetch) {
            for (String token : tokens) {
                String[] cookieKeyValue = token.split(JWTUtils.BASE64_PADDING_CHARACTER_REGEX);
                if (cookieKeyValue[0].equals(JWT)) {
                    boolean isValid =
                            jwtValidator.customHMACValidator(
                                    cookieKeyValue[1],
                                    JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                                    JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
                    Map<String, List<String>> headers = new HashMap<>();
                    headers.put("Set-Cookie", Arrays.asList(token + "; httponly"));
                    ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                            this.getJWTResponseBean(
                                    isValid,
                                    token,
                                    !isValid,
                                    CollectionUtils.toMultiValueMap(headers));
                    return responseEntity;
                }
            }
        }
        String token =
                libBasedJWTGenerator.getHMACSignedJWTToken(
                        JWTUtils.HS256_TOKEN_TO_BE_SIGNED,
                        JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
        Map<String, List<String>> headers = new HashMap<>();
        headers.put("Set-Cookie", Arrays.asList(JWT_COOKIE_KEY + token + "; httponly"));
        ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                this.getJWTResponseBean(
                        true, token, true, CollectionUtils.toMultiValueMap(headers));
        return responseEntity;
    }

    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.CLIENT_SIDE_VULNERABLE_JWT,
            description = "COOKIE_WITH_HTTPONLY_WITHOUT_SECURE_FLAG_BASED_JWT_VULNERABILITY")
    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.INSECURE_CONFIGURATION_JWT,
            description = "COOKIE_BASED_LOW_KEY_STRENGTH_JWT_VULNERABILITY")
    @VulnerableAppRequestMapping(
            value = LevelConstants.LEVEL_4,
            htmlTemplate = "LEVEL_2/JWT_Level2")
    public ResponseEntity<GenericVulnerabilityResponseBean<String>>
            getVulnerablePayloadLevelUnsecure4CookieBased(
                    RequestEntity<String> requestEntity,
                    @RequestParam Map<String, String> queryParams)
                    throws UnsupportedEncodingException, ServiceApplicationException {
        JWTAlgorithmKMS jwtAlgorithmKMS = new JWTAlgorithmKMS();
        Optional<SymmetricAlgorithmKey> symmetricAlgorithmKey =
                jwtAlgorithmKMS.getSymmetricAlgorithmKey(
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.LOW);
        LOGGER.info(symmetricAlgorithmKey.isPresent() + " " + symmetricAlgorithmKey.get());
        List<String> tokens = requestEntity.getHeaders().get("cookie");
        boolean isFetch = Boolean.valueOf(queryParams.get("fetch"));
        if (!isFetch) {
            for (String token : tokens) {
                String[] cookieKeyValue = token.split(JWTUtils.BASE64_PADDING_CHARACTER_REGEX);
                if (cookieKeyValue[0].equals(JWT)) {
                    boolean isValid =
                            jwtValidator.customHMACValidator(
                                    cookieKeyValue[1],
                                    JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                                    JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
                    Map<String, List<String>> headers = new HashMap<>();
                    headers.put("Set-Cookie", Arrays.asList(token + "; httponly"));
                    ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                            this.getJWTResponseBean(
                                    isValid,
                                    token,
                                    !isValid,
                                    CollectionUtils.toMultiValueMap(headers));
                    return responseEntity;
                }
            }
        }

        String token =
                libBasedJWTGenerator.getHMACSignedJWTToken(
                        JWTUtils.HS256_TOKEN_TO_BE_SIGNED,
                        JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
        Map<String, List<String>> headers = new HashMap<>();
        headers.put("Set-Cookie", Arrays.asList(JWT_COOKIE_KEY + token + "; httponly"));
        ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                this.getJWTResponseBean(
                        true, token, true, CollectionUtils.toMultiValueMap(headers));
        return responseEntity;
    }

    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.CLIENT_SIDE_VULNERABLE_JWT,
            description = "COOKIE_WITH_HTTPONLY_WITHOUT_SECURE_FLAG_BASED_JWT_VULNERABILITY")
    @AttackVector(
            vulnerabilityExposed = {VulnerabilityType.SERVER_SIDE_VULNERABLE_JWT},
            description = "COOKIE_BASED_NULL_BYTE_JWT_VULNERABILITY")
    @VulnerableAppRequestMapping(
            value = LevelConstants.LEVEL_5,
            htmlTemplate = "LEVEL_2/JWT_Level2")
    public ResponseEntity<GenericVulnerabilityResponseBean<String>>
            getVulnerablePayloadLevelUnsecure5CookieBased(
                    RequestEntity<String> requestEntity,
                    @RequestParam Map<String, String> queryParams)
                    throws UnsupportedEncodingException, ServiceApplicationException {
        JWTAlgorithmKMS jwtAlgorithmKMS = new JWTAlgorithmKMS();
        Optional<SymmetricAlgorithmKey> symmetricAlgorithmKey =
                jwtAlgorithmKMS.getSymmetricAlgorithmKey(
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.HIGH);
        LOGGER.info(symmetricAlgorithmKey.isPresent() + " " + symmetricAlgorithmKey.get());
        List<String> tokens = requestEntity.getHeaders().get("cookie");
        boolean isFetch = Boolean.valueOf(queryParams.get("fetch"));
        if (!isFetch) {
            for (String token : tokens) {
                String[] cookieKeyValue = token.split(JWTUtils.BASE64_PADDING_CHARACTER_REGEX);
                if (cookieKeyValue[0].equals(JWT)) {
                    boolean isValid =
                            jwtValidator.customHMACNullByteVulnerableValidator(
                                    cookieKeyValue[1],
                                    JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                                    JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
                    Map<String, List<String>> headers = new HashMap<>();
                    headers.put("Set-Cookie", Arrays.asList(token + "; httponly"));
                    ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                            this.getJWTResponseBean(
                                    isValid,
                                    token,
                                    !isValid,
                                    CollectionUtils.toMultiValueMap(headers));
                    return responseEntity;
                }
            }
        }

        String token =
                libBasedJWTGenerator.getHMACSignedJWTToken(
                        JWTUtils.HS256_TOKEN_TO_BE_SIGNED,
                        JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
        Map<String, List<String>> headers = new HashMap<>();
        headers.put("Set-Cookie", Arrays.asList(JWT_COOKIE_KEY + token + "; httponly"));
        ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                this.getJWTResponseBean(
                        true, token, true, CollectionUtils.toMultiValueMap(headers));
        return responseEntity;
    }

    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.CLIENT_SIDE_VULNERABLE_JWT,
            description = "COOKIE_WITH_HTTPONLY_WITHOUT_SECURE_FLAG_BASED_JWT_VULNERABILITY")
    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.SERVER_SIDE_VULNERABLE_JWT,
            description = "COOKIE_BASED_NONE_ALGORITHM_JWT_VULNERABILITY",
            payload = "NONE_ALGORITHM_ATTACK_CURL_PAYLOAD")
    @VulnerableAppRequestMapping(
            value = LevelConstants.LEVEL_6,
            htmlTemplate = "LEVEL_2/JWT_Level2")
    public ResponseEntity<GenericVulnerabilityResponseBean<String>>
            getVulnerablePayloadLevelUnsecure6CookieBased(
                    RequestEntity<String> requestEntity,
                    @RequestParam Map<String, String> queryParams)
                    throws UnsupportedEncodingException, ServiceApplicationException {
        JWTAlgorithmKMS jwtAlgorithmKMS = new JWTAlgorithmKMS();
        Optional<SymmetricAlgorithmKey> symmetricAlgorithmKey =
                jwtAlgorithmKMS.getSymmetricAlgorithmKey(
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.HIGH);
        LOGGER.info(symmetricAlgorithmKey.isPresent() + " " + symmetricAlgorithmKey.get());
        List<String> tokens = requestEntity.getHeaders().get("cookie");
        boolean isFetch = Boolean.valueOf(queryParams.get("fetch"));
        if (!isFetch) {
            for (String token : tokens) {
                String[] cookieKeyValue = token.split(JWTUtils.BASE64_PADDING_CHARACTER_REGEX);
                if (cookieKeyValue[0].equals(JWT)) {
                    boolean isValid =
                            jwtValidator.customHMACNoneAlgorithmVulnerableValidator(
                                    cookieKeyValue[1],
                                    JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                                    JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
                    Map<String, List<String>> headers = new HashMap<>();
                    headers.put("Set-Cookie", Arrays.asList(token + "; httponly"));
                    ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                            this.getJWTResponseBean(
                                    isValid,
                                    token,
                                    !isValid,
                                    CollectionUtils.toMultiValueMap(headers));
                    return responseEntity;
                }
            }
        }

        String token =
                libBasedJWTGenerator.getHMACSignedJWTToken(
                        JWTUtils.HS256_TOKEN_TO_BE_SIGNED,
                        JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
        Map<String, List<String>> headers = new HashMap<>();
        headers.put("Set-Cookie", Arrays.asList(JWT_COOKIE_KEY + token + "; httponly"));
        ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                this.getJWTResponseBean(
                        true, token, true, CollectionUtils.toMultiValueMap(headers));
        return responseEntity;
    }

    // This is a special vulnerability only for scanners as scanners generally don't touch
    // Authorization header
    // as in most of the cases it is not useful and breaks the scanrule logic. For JWT it is a very
    // important
    // header. Issue: https://github.com/SasanLabs/owasp-zap-jwt-addon/issues/31
    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.CLIENT_SIDE_VULNERABLE_JWT,
            description = "COOKIE_CONTAINING_JWT_TOKEN_SECURITY_ATTRIBUTES_MISSING")
    @VulnerableAppRequestMapping(value = LevelConstants.LEVEL_7, htmlTemplate = "LEVEL_7/JWT_Level")
    public ResponseEntity<GenericVulnerabilityResponseBean<String>>
            getVulnerablePayloadLevelUnsecure7CookieBased(
                    RequestEntity<String> requestEntity,
                    @RequestParam Map<String, String> queryParams)
                    throws UnsupportedEncodingException, ServiceApplicationException {
        JWTAlgorithmKMS jwtAlgorithmKMS = new JWTAlgorithmKMS();
        Optional<SymmetricAlgorithmKey> symmetricAlgorithmKey =
                jwtAlgorithmKMS.getSymmetricAlgorithmKey(
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.HIGH);
        LOGGER.info(symmetricAlgorithmKey.isPresent() + " " + symmetricAlgorithmKey.get());
        List<String> tokens = requestEntity.getHeaders().get(HttpHeaders.AUTHORIZATION);
        boolean isFetch = Boolean.valueOf(queryParams.get("fetch"));
        if (!isFetch) {
            for (String token : tokens) {
                boolean isValid =
                        jwtValidator.customHMACNoneAlgorithmVulnerableValidator(
                                token,
                                JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                                JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
                Map<String, List<String>> headers = new HashMap<>();
                headers.put(HttpHeaders.AUTHORIZATION, Arrays.asList(token));
                ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                        this.getJWTResponseBean(
                                isValid, token, !isValid, CollectionUtils.toMultiValueMap(headers));
                return responseEntity;
            }
        }

        String token =
                libBasedJWTGenerator.getHMACSignedJWTToken(
                        JWTUtils.HS256_TOKEN_TO_BE_SIGNED,
                        JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
        Map<String, List<String>> headers = new HashMap<>();
        headers.put(HttpHeaders.AUTHORIZATION, Arrays.asList(token));
        ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                this.getJWTResponseBean(
                        true, token, true, CollectionUtils.toMultiValueMap(headers));
        return responseEntity;
    }

    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.CLIENT_SIDE_VULNERABLE_JWT,
            description = "COOKIE_WITH_HTTPONLY_WITHOUT_SECURE_FLAG_BASED_JWT_VULNERABILITY")
    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.SERVER_SIDE_VULNERABLE_JWT,
            description = "COOKIE_BASED_KEY_CONFUSION_JWT_VULNERABILITY")
    @VulnerableAppRequestMapping(
            value = LevelConstants.LEVEL_8,
            htmlTemplate = "LEVEL_2/JWT_Level2")
    public ResponseEntity<GenericVulnerabilityResponseBean<String>>
            getVulnerablePayloadLevelUnsecure8CookieBased(
                    RequestEntity<String> requestEntity,
                    @RequestParam Map<String, String> queryParams)
                    throws UnsupportedEncodingException, ServiceApplicationException {
        JWTAlgorithmKMS jwtAlgorithmKMS = new JWTAlgorithmKMS();
        Optional<KeyPair> asymmetricAlgorithmKeyPair =
                jwtAlgorithmKMS.getAsymmetricAlgorithmKey("RS256");
        LOGGER.info(
                asymmetricAlgorithmKeyPair.isPresent() + " " + asymmetricAlgorithmKeyPair.get());
        List<String> tokens = requestEntity.getHeaders().get("cookie");
        boolean isFetch = Boolean.valueOf(queryParams.get("fetch"));
        if (!isFetch) {
            for (String token : tokens) {
                String[] cookieKeyValue = token.split(JWTUtils.BASE64_PADDING_CHARACTER_REGEX);
                if (cookieKeyValue[0].equals(JWT)) {
                    boolean isValid =
                            jwtValidator.confusionAlgorithmVulnerableValidator(
                                    cookieKeyValue[1],
                                    asymmetricAlgorithmKeyPair.get().getPublic());
                    Map<String, List<String>> headers = new HashMap<>();
                    headers.put("Set-Cookie", Arrays.asList(token + "; httponly"));
                    ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                            this.getJWTResponseBean(
                                    isValid,
                                    token,
                                    !isValid,
                                    CollectionUtils.toMultiValueMap(headers));

                    return responseEntity;
                }
            }
        }

        String token =
                libBasedJWTGenerator.getJWTToken_RS256(
                        JWTUtils.RS256_TOKEN_TO_BE_SIGNED,
                        asymmetricAlgorithmKeyPair.get().getPrivate());
        Map<String, List<String>> headers = new HashMap<>();
        headers.put("Set-Cookie", Arrays.asList(JWT_COOKIE_KEY + token + "; httponly"));
        ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                this.getJWTResponseBean(
                        true, token, true, CollectionUtils.toMultiValueMap(headers));
        return responseEntity;
    }

    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.CLIENT_SIDE_VULNERABLE_JWT,
            description = "COOKIE_WITH_HTTPONLY_WITHOUT_SECURE_FLAG_BASED_JWT_VULNERABILITY")
    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.SERVER_SIDE_VULNERABLE_JWT,
            description = "COOKIE_BASED_FOR_JWK_HEADER_BASED_JWT_VULNERABILITY")
    // https://nvd.nist.gov/vuln/detail/CVE-2018-0114
    @VulnerableAppRequestMapping(
            value = LevelConstants.LEVEL_9,
            htmlTemplate = "LEVEL_2/JWT_Level2")
    public ResponseEntity<GenericVulnerabilityResponseBean<String>>
            getVulnerablePayloadLevelUnsecure9CookieBased(
                    RequestEntity<String> requestEntity,
                    @RequestParam Map<String, String> queryParams)
                    throws UnsupportedEncodingException, ServiceApplicationException {
        JWTAlgorithmKMS jwtAlgorithmKMS = new JWTAlgorithmKMS();
        Optional<KeyPair> asymmetricAlgorithmKeyPair =
                jwtAlgorithmKMS.getAsymmetricAlgorithmKey("RS256");
        LOGGER.info(
                asymmetricAlgorithmKeyPair.isPresent() + " " + asymmetricAlgorithmKeyPair.get());
        List<String> tokens = requestEntity.getHeaders().get("cookie");
        boolean isFetch = Boolean.valueOf(queryParams.get("fetch"));
        if (!isFetch) {
            for (String token : tokens) {
                String[] cookieKeyValue = token.split(JWTUtils.BASE64_PADDING_CHARACTER_REGEX);
                if (cookieKeyValue[0].equals(JWT)) {
                    boolean isValid =
                            jwtValidator.jwkKeyHeaderPublicKeyTrustingVulnerableValidator(
                                    cookieKeyValue[1]);
                    Map<String, List<String>> headers = new HashMap<>();
                    headers.put("Set-Cookie", Arrays.asList(token + "; httponly"));
                    ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                            this.getJWTResponseBean(
                                    isValid,
                                    token,
                                    !isValid,
                                    CollectionUtils.toMultiValueMap(headers));
                    return responseEntity;
                }
            }
        }

        JWK jwk =
                new RSAKey.Builder((RSAPublicKey) asymmetricAlgorithmKeyPair.get().getPublic())
                        .keyUse(KeyUse.SIGNATURE)
                        .keyID(UUID.randomUUID().toString())
                        .build();
        JSONObject header = new JSONObject();
        header.put(JWTUtils.JWT_ALGORITHM_KEY_HEADER, "RS256");
        header.put("typ", "JWT");
        header.put(JWTUtils.JSON_WEB_KEY_HEADER, new JSONObject(jwk.toJSONString()));

        String base64EncodedHeader =
                JWTUtils.getBase64UrlSafeWithoutPaddingEncodedString(header.toString());
        String token =
                libBasedJWTGenerator.getJWTToken_RS256(
                        base64EncodedHeader
                                + JWTUtils.JWT_TOKEN_PERIOD_CHARACTER
                                + GENERIC_BASE64_ENCODED_PAYLOAD,
                        asymmetricAlgorithmKeyPair.get().getPrivate());
        Map<String, List<String>> headers = new HashMap<>();
        headers.put("Set-Cookie", Arrays.asList(JWT_COOKIE_KEY + token + "; httponly"));
        ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                this.getJWTResponseBean(
                        true, token, true, CollectionUtils.toMultiValueMap(headers));
        return responseEntity;
    }

    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.CLIENT_SIDE_VULNERABLE_JWT,
            description = "COOKIE_WITH_HTTPONLY_WITHOUT_SECURE_FLAG_BASED_JWT_VULNERABILITY")
    @AttackVector(
            vulnerabilityExposed = VulnerabilityType.SERVER_SIDE_VULNERABLE_JWT,
            description = "COOKIE_BASED_EMPTY_TOKEN_JWT_VULNERABILITY")
    @VulnerableAppRequestMapping(
            value = LevelConstants.LEVEL_10,
            htmlTemplate = "LEVEL_2/JWT_Level2")
    public ResponseEntity<GenericVulnerabilityResponseBean<String>>
            getVulnerablePayloadLevelUnsecure10CookieBased(
                    RequestEntity<String> requestEntity,
                    @RequestParam Map<String, String> queryParams)
                    throws UnsupportedEncodingException, ServiceApplicationException {
        JWTAlgorithmKMS jwtAlgorithmKMS = new JWTAlgorithmKMS();
        Optional<SymmetricAlgorithmKey> symmetricAlgorithmKey =
                jwtAlgorithmKMS.getSymmetricAlgorithmKey(
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.HIGH);
        LOGGER.info(symmetricAlgorithmKey.isPresent() + " " + symmetricAlgorithmKey.get());
        List<String> tokens = requestEntity.getHeaders().get("cookie");
        boolean isFetch = Boolean.valueOf(queryParams.get("fetch"));
        if (!isFetch) {
            for (String token : tokens) {
                String[] cookieKeyValue = token.split(JWTUtils.BASE64_PADDING_CHARACTER_REGEX);
                if (cookieKeyValue[0].equals(JWT)) {
                    boolean isValid =
                            jwtValidator.customHMACEmptyTokenVulnerableValidator(
                                    cookieKeyValue[1],
                                    symmetricAlgorithmKey.get().getKey(),
                                    JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
                    Map<String, List<String>> headers = new HashMap<>();
                    headers.put("Set-Cookie", Arrays.asList(token + "; httponly"));
                    ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                            this.getJWTResponseBean(
                                    isValid,
                                    token,
                                    !isValid,
                                    CollectionUtils.toMultiValueMap(headers));
                    return responseEntity;
                }
            }
        }

        String token =
                libBasedJWTGenerator.getHMACSignedJWTToken(
                        JWTUtils.HS256_TOKEN_TO_BE_SIGNED,
                        JWTUtils.getBytes(symmetricAlgorithmKey.get().getKey()),
                        JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
        Map<String, List<String>> headers = new HashMap<>();
        headers.put("Set-Cookie", Arrays.asList(JWT_COOKIE_KEY + token + "; httponly"));
        ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                this.getJWTResponseBean(
                        true, token, true, CollectionUtils.toMultiValueMap(headers));
        return responseEntity;
    }

    // Commented for now because this is not fully developed
    // @AttackVector(
    // vulnerabilityExposed = {VulnerabilitySubType.CLIENT_SIDE_VULNERABLE_JWT},
    // description =
    // "COOKIE_WITH_HTTPONLY_WITHOUT_SECURE_FLAG_BASED_JWT_VULNERABILITY")
    // @AttackVector(
    // vulnerabilityExposed = {VulnerabilitySubType.INSECURE_CONFIGURATION_JWT,
    // VulnerabilitySubType.BLIND_SQL_INJECTION},
    // description = "COOKIE_BASED_EMPTY_TOKEN_JWT_VULNERABILITY")
    // @VulnerabilityLevel(
    // value = LevelEnum.LEVEL_10,
    // descriptionLabel = "COOKIE_CONTAINING_JWT_TOKEN",
    // htmlTemplate = "LEVEL_2/JWT_Level2",
    // parameterName = JWT,
    // requestParameterLocation = RequestParameterLocation.COOKIE,
    public ResponseEntity<GenericVulnerabilityResponseBean<String>>
            getVulnerablePayloadLevelUnsecure11CookieBased(
                    RequestEntity<String> requestEntity,
                    @RequestParam Map<String, String> queryParams)
                    throws UnsupportedEncodingException, ServiceApplicationException {
        List<String> tokens = requestEntity.getHeaders().get("cookie");
        boolean isFetch = Boolean.valueOf(queryParams.get("fetch"));
        if (!isFetch) {
            for (String token : tokens) {
                String[] cookieKeyValue = token.split(JWTUtils.BASE64_PADDING_CHARACTER_REGEX);
                if (cookieKeyValue[0].equals(JWT)) {
                    RSAPublicKey rsaPublicKey =
                            JWTUtils.getRSAPublicKeyFromProvidedPEMFilePath(
                                    this.getClass()
                                            .getClassLoader()
                                            .getResourceAsStream(
                                                    JWTUtils.KEYS_LOCATION + "public_crt.pem"));
                    boolean isValid =
                            this.jwtValidator.genericJWTTokenValidator(
                                    cookieKeyValue[1], rsaPublicKey, "RS256");
                    Map<String, List<String>> headers = new HashMap<>();
                    headers.put("Set-Cookie", Arrays.asList(token + "; httponly"));
                    ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                            this.getJWTResponseBean(
                                    isValid,
                                    token,
                                    !isValid,
                                    CollectionUtils.toMultiValueMap(headers));
                    return responseEntity;
                }
            }
        }
        RSAPrivateKey rsaPrivateKey =
                JWTUtils.getRSAPrivateKeyFromProvidedPEMFilePath(
                        this.getClass()
                                .getClassLoader()
                                .getResourceAsStream(JWTUtils.KEYS_LOCATION + "private_key.pem"));
        String token =
                libBasedJWTGenerator.getJWTToken_RS256(
                        JWTUtils.RS256_TOKEN_TO_BE_SIGNED, rsaPrivateKey);
        Map<String, List<String>> headers = new HashMap<>();
        headers.put("Set-Cookie", Arrays.asList(JWT_COOKIE_KEY + token + "; httponly"));
        ResponseEntity<GenericVulnerabilityResponseBean<String>> responseEntity =
                this.getJWTResponseBean(
                        true, token, true, CollectionUtils.toMultiValueMap(headers));
        return responseEntity;
    }
}
