From 937ab417b1bad3ba63e855e3b666453f8e1c4ab4 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 4 May 2022 22:46:01 +0000 Subject: [PATCH 1/4] Query to detect hardcoded JWT secret keys --- .../Security/CWE/CWE-321/HardcodedJwtKey.java | 26 ++ .../CWE/CWE-321/HardcodedJwtKey.qhelp | 46 ++ .../Security/CWE/CWE-321/HardcodedJwtKey.ql | 19 + .../Security/CWE/CWE-321/HardcodedJwtKey.qll | 156 +++++++ .../security/CWE-321/HardcodedJwtKey.expected | 25 ++ .../security/CWE-321/HardcodedJwtKey.java | 65 +++ .../security/CWE-321/HardcodedJwtKey.qlref | 1 + .../query-tests/security/CWE-321/options | 1 + .../auth0-jwt-2.3/com/auth0/jwt/JWT.java | 57 +++ .../com/auth0/jwt/JWTCreator.java | 300 +++++++++++++ .../com/auth0/jwt/algorithms/Algorithm.java | 397 ++++++++++++++++++ .../jwt/exceptions/JWTCreationException.java | 6 + .../jwt/exceptions/JWTDecodeException.java | 9 + .../exceptions/JWTVerificationException.java | 9 + .../SignatureGenerationException.java | 8 + .../SignatureVerificationException.java | 13 + .../com/auth0/jwt/interfaces/DecodedJWT.java | 37 ++ .../jwt/interfaces/ECDSAKeyProvider.java | 10 + .../com/auth0/jwt/interfaces/JWTVerifier.java | 25 ++ .../com/auth0/jwt/interfaces/KeyProvider.java | 35 ++ .../auth0/jwt/interfaces/RSAKeyProvider.java | 10 + .../auth0/jwt/interfaces/Verification.java | 189 +++++++++ 22 files changed, 1444 insertions(+) create mode 100644 java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.java create mode 100644 java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qhelp create mode 100644 java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.ql create mode 100644 java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll create mode 100644 java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.expected create mode 100644 java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.java create mode 100644 java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.qlref create mode 100644 java/ql/test/experimental/query-tests/security/CWE-321/options create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/JWT.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/JWTCreator.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/algorithms/Algorithm.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTCreationException.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTDecodeException.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTVerificationException.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/SignatureGenerationException.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/SignatureVerificationException.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/DecodedJWT.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/ECDSAKeyProvider.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/JWTVerifier.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/KeyProvider.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/RSAKeyProvider.java create mode 100644 java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/Verification.java diff --git a/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.java b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.java new file mode 100644 index 000000000000..697127946895 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.java @@ -0,0 +1,26 @@ +// BAD: Get secret from hardcoded string then sign a JWT token +Algorithm algorithm = Algorithm.HMAC256("hardcoded_secret"); +JWT.create() + .withClaim("username", username) + .sign(algorithm); +} + +// BAD: Get secret from hardcoded string then verify a JWT token +JWTVerifier verifier = JWT.require(Algorithm.HMAC256("hardcoded_secret")) + .withIssuer(ISSUER) + .build(); +verifier.verify(token); + +// GOOD: Get secret from system configuration then sign a token +String tokenSecret = System.getenv("SECRET_KEY"); +Algorithm algorithm = Algorithm.HMAC256(tokenSecret); +JWT.create() + .withClaim("username", username) + .sign(algorithm); + } + +// GOOD: Get secret from environment variable then verify a JWT token +JWTVerifier verifier = JWT.require(Algorithm.HMAC256(System.getenv("SECRET_KEY"))) + .withIssuer(ISSUER) + .build(); +verifier.verify(token); diff --git a/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qhelp b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qhelp new file mode 100644 index 000000000000..b8e984280be4 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qhelp @@ -0,0 +1,46 @@ + + + +

+ JWT (JSON Web Token) is an open standard (RFC 7519) that defines a way to provide information + within a JSON object between two parties. JWT is widely used for sharing security information + between two parties in web applications. Each JWT contains encoded JSON objects, including a + set of claims. JWTs are signed using a cryptographic algorithm to ensure that the claims cannot + be altered after the token is issued. +

+

+ The most basic mistake is using hardcoded secrets for JWT generation/verification. This allows + an attacker to forge the token if the source code (and JWT secret in it) is publicly exposed or + leaked, which leads to authentication bypass or privilege escalation. +

+
+ + +

+ Generating a cryptographically secure secret key during application initialization and using this + generated key for JWT signing/verification requests can prevent this vulnerability. Or safely store + the secret key in a key vault that cannot be leaked in source code. +

+
+ + +

+ The following examples show the bad case and the good case respectively. The bad + methods show a hardcoded secret key is used to sign and verify JWT tokens. In the good + method, the secret key is loaded from a system environment during application initialization. +

+ +
+ + +
  • + Semgrep Blog: + Hardcoded secrets, unverified tokens, and other common JWT mistakes +
  • +
  • + CVE-2022-24860: + Databasir 1.01 has Use of Hard-coded Cryptographic Key vulnerability. +
  • +
    + +
    \ No newline at end of file diff --git a/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.ql b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.ql new file mode 100644 index 000000000000..63c55793cbf9 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.ql @@ -0,0 +1,19 @@ +/** + * @name Use of a hardcoded key for signing JWT + * @description Using a hardcoded key for signing JWT can allow an attacker to compromise security. + * @kind path-problem + * @problem.severity error + * @id java/hardcoded-jwt-key + * @tags security + * external/cwe/cwe-321 + */ + +import java +import HardcodedJwtKey +import semmle.code.java.dataflow.TaintTracking +import DataFlow::PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, HardcodedJwtKeyConfiguration cfg +where cfg.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ is used to sign a JWT token.", source.getNode(), + "Hardcoded String" diff --git a/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll new file mode 100644 index 000000000000..c960b065215d --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll @@ -0,0 +1,156 @@ +/** + * Provides sources and sinks for detecting JWT token signing vulnerabilities. + */ + +import java +private import semmle.code.java.dataflow.FlowSources + +/** The Java class `com.auth0.jwt.JWT`. */ +class Jwt extends RefType { + Jwt() { this.hasQualifiedName("com.auth0.jwt", "JWT") } +} + +/** The Java class `com.auth0.jwt.JWTCreator.Builder`. */ +class JwtBuilder extends RefType { + JwtBuilder() { this.hasQualifiedName("com.auth0.jwt", "JWTCreator$Builder") } +} + +/** The Java class `com.auth0.jwt.algorithms.Algorithm`. */ +class Algorithm extends RefType { + Algorithm() { this.hasQualifiedName("com.auth0.jwt.algorithms", "Algorithm") } +} + +/** + * The Java interface `com.auth0.jwt.interfaces.JWTVerifier` or it implementation class + * `com.auth0.jwt.JWTVerifier`. + */ +class JwtVerifier extends RefType { + JwtVerifier() { + this.hasQualifiedName(["com.auth0.jwt", "com.auth0.jwt.interfaces"], "JWTVerifier") + } +} + +/** The secret generation method declared in `com.auth0.jwt.algorithms.Algorithm`. */ +class GetSecretMethod extends Method { + GetSecretMethod() { + this.getDeclaringType() instanceof Algorithm and + ( + this.getName().substring(0, 4) = "HMAC" or + this.getName().substring(0, 5) = "ECDSA" or + this.getName().substring(0, 3) = "RSA" + ) + } +} + +/** The `require` method of `com.auth0.jwt.JWT`. */ +class RequireMethod extends Method { + RequireMethod() { + this.getDeclaringType() instanceof Jwt and + this.hasName("require") + } +} + +/** The `sign` method of `com.auth0.jwt.JWTCreator.Builder`. */ +class SignTokenMethod extends Method { + SignTokenMethod() { + this.getDeclaringType() instanceof JwtBuilder and + this.hasName("sign") + } +} + +/** The `verify` method of `com.auth0.jwt.interfaces.JWTVerifier`. */ +class VerifyTokenMethod extends Method { + VerifyTokenMethod() { + this.getDeclaringType() instanceof JwtVerifier and + this.hasName("verify") + } +} + +/** + * A data flow source for JWT token signing vulnerabilities. + */ +abstract class JwtKeySource extends DataFlow::Node { } + +/** + * A data flow sink for JWT token signing vulnerabilities. + */ +abstract class JwtTokenSink extends DataFlow::Node { } + +private predicate isTestCode(Expr e) { + e.getFile().getAbsolutePath().toLowerCase().matches("%test%") and + not e.getFile().getAbsolutePath().toLowerCase().matches("%ql/test%") +} + +/** + * A hardcoded string literal as a source for JWT token signing vulnerabilities. + */ +class HardcodedKeyStringSource extends JwtKeySource { + HardcodedKeyStringSource() { + this.asExpr() instanceof CompileTimeConstantExpr and + not isTestCode(this.asExpr()) + } +} + +/** + * An expression used to sign JWT tokens as a sink of JWT token signing vulnerabilities. + */ +private class SignTokenSink extends JwtTokenSink { + SignTokenSink() { + exists(MethodAccess ma | + ma.getMethod() instanceof SignTokenMethod and + this.asExpr() = ma.getArgument(0) + ) + } +} + +/** + * An expression used to verify JWT tokens as a sink of JWT token signing vulnerabilities. + */ +private class VerifyTokenSink extends JwtTokenSink { + VerifyTokenSink() { + exists(MethodAccess ma | + ma.getMethod() instanceof VerifyTokenMethod and + this.asExpr() = ma.getQualifier() + ) + } +} + +/** + * A configuration depicting taint flow for checking JWT token signing vulnerabilities. + */ +class HardcodedJwtKeyConfiguration extends TaintTracking::Configuration { + HardcodedJwtKeyConfiguration() { this = "Hard-coded JWT Signing Key" } + + override predicate isSource(DataFlow::Node source) { source instanceof JwtKeySource } + + override predicate isSink(DataFlow::Node sink) { sink instanceof JwtTokenSink } + + override predicate isAdditionalTaintStep(DataFlow::Node prev, DataFlow::Node succ) { + exists(MethodAccess ma | + ( + ma.getMethod() instanceof GetSecretMethod or + ma.getMethod() instanceof RequireMethod + ) and + prev.asExpr() = ma.getArgument(0) and + succ.asExpr() = ma + ) + } +} + +/** Taint model related to verifying JWT tokens. */ +private class VerificationFlowStep extends SummaryModelCsv { + override predicate row(string row) { + row = + [ + "com.auth0.jwt.interfaces;Verification;true;build;;;Argument[-1];ReturnValue;taint", + "com.auth0.jwt.interfaces;Verification;true;" + + ["acceptLeeway", "acceptExpiresAt", "acceptNotBefore", "acceptIssuedAt", "ignoreIssuedAt"] + + ";;;Argument[-1];ReturnValue;taint", + "com.auth0.jwt.interfaces;Verification;true;with" + + [ + "Issuer", "Subject", "Audience", "AnyOfAudience", "ClaimPresence", "Claim", + "ArrayClaim", "JWTId" + ] + ";;;Argument[-1];ReturnValue;taint" + ] + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.expected b/java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.expected new file mode 100644 index 000000000000..15e882db4cdd --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.expected @@ -0,0 +1,25 @@ +edges +| HardcodedJwtKey.java:15:33:15:38 | SECRET : String | HardcodedJwtKey.java:19:49:19:54 | SECRET : String | +| HardcodedJwtKey.java:15:33:15:38 | SECRET : String | HardcodedJwtKey.java:42:62:42:67 | SECRET : String | +| HardcodedJwtKey.java:15:42:15:59 | "hardcoded_secret" : String | HardcodedJwtKey.java:15:33:15:38 | SECRET : String | +| HardcodedJwtKey.java:19:49:19:54 | SECRET : String | HardcodedJwtKey.java:25:23:25:31 | algorithm | +| HardcodedJwtKey.java:42:32:42:69 | require(...) : Verification | HardcodedJwtKey.java:42:32:43:35 | withIssuer(...) : Verification | +| HardcodedJwtKey.java:42:32:43:35 | withIssuer(...) : Verification | HardcodedJwtKey.java:42:32:44:24 | build(...) : JWTVerifier | +| HardcodedJwtKey.java:42:32:44:24 | build(...) : JWTVerifier | HardcodedJwtKey.java:46:13:46:20 | verifier | +| HardcodedJwtKey.java:42:62:42:67 | SECRET : String | HardcodedJwtKey.java:42:32:42:69 | require(...) : Verification | +nodes +| HardcodedJwtKey.java:15:33:15:38 | SECRET : String | semmle.label | SECRET : String | +| HardcodedJwtKey.java:15:42:15:59 | "hardcoded_secret" : String | semmle.label | "hardcoded_secret" : String | +| HardcodedJwtKey.java:19:49:19:54 | SECRET : String | semmle.label | SECRET : String | +| HardcodedJwtKey.java:25:23:25:31 | algorithm | semmle.label | algorithm | +| HardcodedJwtKey.java:42:32:42:69 | require(...) : Verification | semmle.label | require(...) : Verification | +| HardcodedJwtKey.java:42:32:43:35 | withIssuer(...) : Verification | semmle.label | withIssuer(...) : Verification | +| HardcodedJwtKey.java:42:32:44:24 | build(...) : JWTVerifier | semmle.label | build(...) : JWTVerifier | +| HardcodedJwtKey.java:42:62:42:67 | SECRET : String | semmle.label | SECRET : String | +| HardcodedJwtKey.java:46:13:46:20 | verifier | semmle.label | verifier | +subpaths +#select +| HardcodedJwtKey.java:25:23:25:31 | algorithm | HardcodedJwtKey.java:15:42:15:59 | "hardcoded_secret" : String | HardcodedJwtKey.java:25:23:25:31 | algorithm | $@ is used to sign a JWT token. | HardcodedJwtKey.java:15:42:15:59 | "hardcoded_secret" | Hardcoded String | +| HardcodedJwtKey.java:25:23:25:31 | algorithm | HardcodedJwtKey.java:19:49:19:54 | SECRET : String | HardcodedJwtKey.java:25:23:25:31 | algorithm | $@ is used to sign a JWT token. | HardcodedJwtKey.java:19:49:19:54 | SECRET | Hardcoded String | +| HardcodedJwtKey.java:46:13:46:20 | verifier | HardcodedJwtKey.java:15:42:15:59 | "hardcoded_secret" : String | HardcodedJwtKey.java:46:13:46:20 | verifier | $@ is used to sign a JWT token. | HardcodedJwtKey.java:15:42:15:59 | "hardcoded_secret" | Hardcoded String | +| HardcodedJwtKey.java:46:13:46:20 | verifier | HardcodedJwtKey.java:42:62:42:67 | SECRET : String | HardcodedJwtKey.java:46:13:46:20 | verifier | $@ is used to sign a JWT token. | HardcodedJwtKey.java:42:62:42:67 | SECRET | Hardcoded String | diff --git a/java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.java b/java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.java new file mode 100644 index 000000000000..cfcb027755bf --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.java @@ -0,0 +1,65 @@ +import java.util.Date; +import java.util.Properties; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.JWTVerifier; + +public class HardcodedJwtKey { + // 15 minutes + private static final long ACCESS_EXPIRE_TIME = 1000 * 60 * 15; + + private static final String ISSUER = "example_com"; + + private static final String SECRET = "hardcoded_secret"; + + // BAD: Get secret from hardcoded string then sign a JWT token + public String accessTokenBad(String username) { + Algorithm algorithm = Algorithm.HMAC256(SECRET); + + return JWT.create() + .withExpiresAt(new Date(new Date().getTime() + ACCESS_EXPIRE_TIME)) + .withIssuer(ISSUER) + .withClaim("username", username) + .sign(algorithm); + } + + // GOOD: Get secret from system configuration then sign a token + public String accessTokenGood(String username) { + String tokenSecret = System.getenv("SECRET_KEY"); + Algorithm algorithm = Algorithm.HMAC256(tokenSecret); + + return JWT.create() + .withExpiresAt(new Date(new Date().getTime() + ACCESS_EXPIRE_TIME)) + .withIssuer(ISSUER) + .withClaim("username", username) + .sign(algorithm); + } + + // BAD: Get secret from hardcoded string then verify a JWT token + public boolean verifyTokenBad(String token) { + JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)) + .withIssuer(ISSUER) + .build(); + try { + verifier.verify(token); + return true; + } catch (JWTVerificationException e) { + return false; + } + } + + // GOOD: Get secret from environment variable then verify a JWT token + public boolean verifyTokenGood(String token) { + JWTVerifier verifier = JWT.require(Algorithm.HMAC256(System.getenv("SECRET_KEY"))) + .withIssuer(ISSUER) + .build(); + try { + verifier.verify(token); + return true; + } catch (JWTVerificationException e) { + return false; + } + } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.qlref b/java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.qlref new file mode 100644 index 000000000000..3da970cd3805 --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-321/HardcodedJwtKey.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-321/HardcodedJwtKey.ql \ No newline at end of file diff --git a/java/ql/test/experimental/query-tests/security/CWE-321/options b/java/ql/test/experimental/query-tests/security/CWE-321/options new file mode 100644 index 000000000000..ab6ca411a02d --- /dev/null +++ b/java/ql/test/experimental/query-tests/security/CWE-321/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/auth0-jwt-2.3 diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/JWT.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/JWT.java new file mode 100644 index 000000000000..bd8054841060 --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/JWT.java @@ -0,0 +1,57 @@ +package com.auth0.jwt; + +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTDecodeException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.Verification; + +public class JWT { + public JWT() { + } + + /** + * Decode a given Json Web Token. + *

    + * Note that this method doesn't verify the token's signature! Use it only if you trust the token or you already verified it. + * + * @param token with jwt format as string. + * @return a decoded JWT. + * @throws JWTDecodeException if any part of the token contained an invalid jwt or JSON format of each of the jwt parts. + */ + public DecodedJWT decodeJwt(String token) throws JWTDecodeException { + return null; + } + + /** + * Decode a given Json Web Token. + *

    + * Note that this method doesn't verify the token's signature! Use it only if you trust the token or you already verified it. + * + * @param token with jwt format as string. + * @return a decoded JWT. + * @throws JWTDecodeException if any part of the token contained an invalid jwt or JSON format of each of the jwt parts. + */ + public static DecodedJWT decode(String token) throws JWTDecodeException { + return null; + } + + /** + * Returns a {@link JWTVerifier} builder with the algorithm to be used to validate token signature. + * + * @param algorithm that will be used to verify the token's signature. + * @return {@link JWTVerifier} builder + * @throws IllegalArgumentException if the provided algorithm is null. + */ + public static Verification require(Algorithm algorithm) { + return null; + } + + /** + * Returns a Json Web Token builder used to create and sign tokens + * + * @return a token builder. + */ + public static JWTCreator.Builder create() { + return null; + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/JWTCreator.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/JWTCreator.java new file mode 100644 index 000000000000..863298425bd6 --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/JWTCreator.java @@ -0,0 +1,300 @@ +package com.auth0.jwt; + +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTCreationException; + +import java.util.*; + +/** + * The JWTCreator class holds the sign method to generate a complete JWT (with Signature) from a given Header and Payload content. + *

    + * This class is thread-safe. + */ +public final class JWTCreator { + /** + * Initialize a JWTCreator instance. + * + * @return a JWTCreator.Builder instance to configure. + */ + static JWTCreator.Builder init() { + return null; + } + + /** + * The Builder class holds the Claims that defines the JWT to be created. + */ + public static class Builder { + Builder() { + } + + /** + * Add specific Claims to set as the Header. + * If provided map is null then nothing is changed + * If provided map contains a claim with null value then that claim will be removed from the header + * + * @param headerClaims the values to use as Claims in the token's Header. + * @return this same Builder instance. + */ + public Builder withHeader(Map headerClaims) { + return null; + } + + /** + * Add a specific Key Id ("kid") claim to the Header. + * If the {@link Algorithm} used to sign this token was instantiated with a KeyProvider, the 'kid' value will be taken from that provider and this one will be ignored. + * + * @param keyId the Key Id value. + * @return this same Builder instance. + */ + public Builder withKeyId(String keyId) { + return null; + } + + /** + * Add a specific Issuer ("iss") claim to the Payload. + * + * @param issuer the Issuer value. + * @return this same Builder instance. + */ + public Builder withIssuer(String issuer) { + return null; + } + + /** + * Add a specific Subject ("sub") claim to the Payload. + * + * @param subject the Subject value. + * @return this same Builder instance. + */ + public Builder withSubject(String subject) { + return null; + } + + /** + * Add a specific Audience ("aud") claim to the Payload. + * + * @param audience the Audience value. + * @return this same Builder instance. + */ + public Builder withAudience(String... audience) { + return null; + } + + /** + * Add a specific Expires At ("exp") claim to the Payload. + * + * @param expiresAt the Expires At value. + * @return this same Builder instance. + */ + public Builder withExpiresAt(Date expiresAt) { + return null; + } + + /** + * Add a specific Not Before ("nbf") claim to the Payload. + * + * @param notBefore the Not Before value. + * @return this same Builder instance. + */ + public Builder withNotBefore(Date notBefore) { + return null; + } + + /** + * Add a specific Issued At ("iat") claim to the Payload. + * + * @param issuedAt the Issued At value. + * @return this same Builder instance. + */ + public Builder withIssuedAt(Date issuedAt) { + return null; + } + + /** + * Add a specific JWT Id ("jti") claim to the Payload. + * + * @param jwtId the Token Id value. + * @return this same Builder instance. + */ + public Builder withJWTId(String jwtId) { + return null; + } + + /** + * Add a custom Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null. + */ + public Builder withClaim(String name, Boolean value) throws IllegalArgumentException { + return null; + } + + /** + * Add a custom Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null. + */ + public Builder withClaim(String name, Integer value) throws IllegalArgumentException { + return null; + } + + /** + * Add a custom Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null. + */ + public Builder withClaim(String name, Long value) throws IllegalArgumentException { + return null; + } + + /** + * Add a custom Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null. + */ + public Builder withClaim(String name, Double value) throws IllegalArgumentException { + return null; + } + + /** + * Add a custom Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null. + */ + public Builder withClaim(String name, String value) throws IllegalArgumentException { + return null; + } + + /** + * Add a custom Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null. + */ + public Builder withClaim(String name, Date value) throws IllegalArgumentException { + return null; + } + + /** + * Add a custom Array Claim with the given items. + * + * @param name the Claim's name. + * @param items the Claim's value. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null. + */ + public Builder withArrayClaim(String name, String[] items) throws IllegalArgumentException { + return null; + } + + /** + * Add a custom Array Claim with the given items. + * + * @param name the Claim's name. + * @param items the Claim's value. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null. + */ + public Builder withArrayClaim(String name, Integer[] items) throws IllegalArgumentException { + return null; + } + + /** + * Add a custom Array Claim with the given items. + * + * @param name the Claim's name. + * @param items the Claim's value. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null + */ + public Builder withArrayClaim(String name, Long[] items) throws IllegalArgumentException { + return null; + } + + /** + * Add a custom Map Claim with the given items. + *

    + * Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types + * {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double}, + * {@linkplain String} and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values. + * {@linkplain List}s can contain null elements. + * + * @param name the Claim's name. + * @param map the Claim's key-values. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null, or if the map contents does not validate. + */ + public Builder withClaim(String name, Map map) throws IllegalArgumentException { + return null; + } + + /** + * Add a custom List Claim with the given items. + *

    + * Accepted nested types are {@linkplain Map} and {@linkplain List} with basic types + * {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double}, + * {@linkplain String} and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values. + * {@linkplain List}s can contain null elements. + * + * @param name the Claim's name. + * @param list the Claim's list of values. + * @return this same Builder instance. + * @throws IllegalArgumentException if the name is null, or if the list contents does not validate. + */ + + public Builder withClaim(String name, List list) throws IllegalArgumentException { + return null; + } + + /** + * Add specific Claims to set as the Payload. If the provided map is null then + * nothing is changed. + *

    + * Accepted types are {@linkplain Map} and {@linkplain List} with basic types + * {@linkplain Boolean}, {@linkplain Integer}, {@linkplain Long}, {@linkplain Double}, + * {@linkplain String} and {@linkplain Date}. {@linkplain Map}s cannot contain null keys or values. + * {@linkplain List}s can contain null elements. + *

    + * + *

    + * If any of the claims are invalid, none will be added. + *

    + * + * @param payloadClaims the values to use as Claims in the token's payload. + * @throws IllegalArgumentException if any of the claim keys or null, or if the values are not of a supported type. + * @return this same Builder instance. + */ + public Builder withPayload(Map payloadClaims) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new JWT and signs is with the given algorithm + * + * @param algorithm used to sign the JWT + * @return a new JWT token + * @throws IllegalArgumentException if the provided algorithm is null. + * @throws JWTCreationException if the claims could not be converted to a valid JSON or there was a problem with the signing key. + */ + public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException { + return null; + } + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/algorithms/Algorithm.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/algorithms/Algorithm.java new file mode 100644 index 000000000000..662043eaab95 --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/algorithms/Algorithm.java @@ -0,0 +1,397 @@ +package com.auth0.jwt.algorithms; + +import com.auth0.jwt.exceptions.SignatureGenerationException; +import com.auth0.jwt.exceptions.SignatureVerificationException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.ECDSAKeyProvider; +import com.auth0.jwt.interfaces.RSAKeyProvider; + +import java.security.interfaces.*; + +/** + * The Algorithm class represents an algorithm to be used in the Signing or Verification process of a Token. + *

    + * This class and its subclasses are thread-safe. + */ +public abstract class Algorithm { + + /** + * Creates a new Algorithm instance using SHA256withRSA. Tokens specify this as "RS256". + * + * @param keyProvider the provider of the Public Key and Private Key for the verify and signing instance. + * @return a valid RSA256 Algorithm. + * @throws IllegalArgumentException if the provided Key is null. + */ + public static Algorithm RSA256(RSAKeyProvider keyProvider) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA256withRSA. Tokens specify this as "RS256". + * + * @param publicKey the key to use in the verify instance. + * @param privateKey the key to use in the signing instance. + * @return a valid RSA256 Algorithm. + * @throws IllegalArgumentException if both provided Keys are null. + */ + public static Algorithm RSA256(RSAPublicKey publicKey, RSAPrivateKey privateKey) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA256withRSA. Tokens specify this as "RS256". + * + * @param key the key to use in the verify or signing instance. + * @return a valid RSA256 Algorithm. + * @throws IllegalArgumentException if the Key Provider is null. + * @deprecated use {@link #RSA256(RSAPublicKey, RSAPrivateKey)} or {@link #RSA256(RSAKeyProvider)} + */ + @Deprecated + public static Algorithm RSA256(RSAKey key) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA384withRSA. Tokens specify this as "RS384". + * + * @param keyProvider the provider of the Public Key and Private Key for the verify and signing instance. + * @return a valid RSA384 Algorithm. + * @throws IllegalArgumentException if the Key Provider is null. + */ + public static Algorithm RSA384(RSAKeyProvider keyProvider) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA384withRSA. Tokens specify this as "RS384". + * + * @param publicKey the key to use in the verify instance. + * @param privateKey the key to use in the signing instance. + * @return a valid RSA384 Algorithm. + * @throws IllegalArgumentException if both provided Keys are null. + */ + public static Algorithm RSA384(RSAPublicKey publicKey, RSAPrivateKey privateKey) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA384withRSA. Tokens specify this as "RS384". + * + * @param key the key to use in the verify or signing instance. + * @return a valid RSA384 Algorithm. + * @throws IllegalArgumentException if the provided Key is null. + * @deprecated use {@link #RSA384(RSAPublicKey, RSAPrivateKey)} or {@link #RSA384(RSAKeyProvider)} + */ + @Deprecated + public static Algorithm RSA384(RSAKey key) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA512withRSA. Tokens specify this as "RS512". + * + * @param keyProvider the provider of the Public Key and Private Key for the verify and signing instance. + * @return a valid RSA512 Algorithm. + * @throws IllegalArgumentException if the Key Provider is null. + */ + public static Algorithm RSA512(RSAKeyProvider keyProvider) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA512withRSA. Tokens specify this as "RS512". + * + * @param publicKey the key to use in the verify instance. + * @param privateKey the key to use in the signing instance. + * @return a valid RSA512 Algorithm. + * @throws IllegalArgumentException if both provided Keys are null. + */ + public static Algorithm RSA512(RSAPublicKey publicKey, RSAPrivateKey privateKey) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA512withRSA. Tokens specify this as "RS512". + * + * @param key the key to use in the verify or signing instance. + * @return a valid RSA512 Algorithm. + * @throws IllegalArgumentException if the provided Key is null. + * @deprecated use {@link #RSA512(RSAPublicKey, RSAPrivateKey)} or {@link #RSA512(RSAKeyProvider)} + */ + @Deprecated + public static Algorithm RSA512(RSAKey key) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using HmacSHA256. Tokens specify this as "HS256". + * + * @param secret the secret to use in the verify or signing instance. + * @return a valid HMAC256 Algorithm. + * @throws IllegalArgumentException if the provided Secret is null. + */ + public static Algorithm HMAC256(String secret) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using HmacSHA384. Tokens specify this as "HS384". + * + * @param secret the secret to use in the verify or signing instance. + * @return a valid HMAC384 Algorithm. + * @throws IllegalArgumentException if the provided Secret is null. + */ + public static Algorithm HMAC384(String secret) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using HmacSHA512. Tokens specify this as "HS512". + * + * @param secret the secret to use in the verify or signing instance. + * @return a valid HMAC512 Algorithm. + * @throws IllegalArgumentException if the provided Secret is null. + */ + public static Algorithm HMAC512(String secret) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using HmacSHA256. Tokens specify this as "HS256". + * + * @param secret the secret bytes to use in the verify or signing instance. + * @return a valid HMAC256 Algorithm. + * @throws IllegalArgumentException if the provided Secret is null. + */ + public static Algorithm HMAC256(byte[] secret) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA256withECDSA. Tokens specify this as "ES256K". + * + * @param keyProvider the provider of the Public Key and Private Key for the verify and signing instance. + * @return a valid ECDSA256 Algorithm. + * @throws IllegalArgumentException if the Key Provider is null. + * @deprecated The SECP-256K1 Curve algorithm has been disabled beginning in Java 15. + * Use of this method in those unsupported Java versions will throw a {@link java.security.SignatureException}. + * This method will be removed in the next major version. See for additional information + */ + @Deprecated + public static Algorithm ECDSA256K(ECDSAKeyProvider keyProvider) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA256withECDSA. Tokens specify this as "ES256K". + * + * @param publicKey the key to use in the verify instance. + * @param privateKey the key to use in the signing instance. + * @return a valid ECDSA256 Algorithm. + * @throws IllegalArgumentException if the provided Key is null. + * @deprecated The SECP-256K1 Curve algorithm has been disabled beginning in Java 15. + * Use of this method in those unsupported Java versions will throw a {@link java.security.SignatureException}. + * This method will be removed in the next major version. See for additional information + */ + @Deprecated + public static Algorithm ECDSA256K(ECPublicKey publicKey, ECPrivateKey privateKey) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using HmacSHA384. Tokens specify this as "HS384". + * + * @param secret the secret bytes to use in the verify or signing instance. + * @return a valid HMAC384 Algorithm. + * @throws IllegalArgumentException if the provided Secret is null. + */ + public static Algorithm HMAC384(byte[] secret) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using HmacSHA512. Tokens specify this as "HS512". + * + * @param secret the secret bytes to use in the verify or signing instance. + * @return a valid HMAC512 Algorithm. + * @throws IllegalArgumentException if the provided Secret is null. + */ + public static Algorithm HMAC512(byte[] secret) throws IllegalArgumentException { + return null; + } + + + + /** + * Creates a new Algorithm instance using SHA256withECDSA. Tokens specify this as "ES256". + * + * @param keyProvider the provider of the Public Key and Private Key for the verify and signing instance. + * @return a valid ECDSA256 Algorithm. + * @throws IllegalArgumentException if the Key Provider is null. + */ + public static Algorithm ECDSA256(ECDSAKeyProvider keyProvider) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA256withECDSA. Tokens specify this as "ES256". + * + * @param publicKey the key to use in the verify instance. + * @param privateKey the key to use in the signing instance. + * @return a valid ECDSA256 Algorithm. + * @throws IllegalArgumentException if the provided Key is null. + */ + public static Algorithm ECDSA256(ECPublicKey publicKey, ECPrivateKey privateKey) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA256withECDSA. Tokens specify this as "ES256". + * + * @param key the key to use in the verify or signing instance. + * @return a valid ECDSA256 Algorithm. + * @throws IllegalArgumentException if the provided Key is null. + * @deprecated use {@link #ECDSA256(ECPublicKey, ECPrivateKey)} or {@link #ECDSA256(ECDSAKeyProvider)} + */ + @Deprecated + public static Algorithm ECDSA256(ECKey key) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA384withECDSA. Tokens specify this as "ES384". + * + * @param keyProvider the provider of the Public Key and Private Key for the verify and signing instance. + * @return a valid ECDSA384 Algorithm. + * @throws IllegalArgumentException if the Key Provider is null. + */ + public static Algorithm ECDSA384(ECDSAKeyProvider keyProvider) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA384withECDSA. Tokens specify this as "ES384". + * + * @param publicKey the key to use in the verify instance. + * @param privateKey the key to use in the signing instance. + * @return a valid ECDSA384 Algorithm. + * @throws IllegalArgumentException if the provided Key is null. + */ + public static Algorithm ECDSA384(ECPublicKey publicKey, ECPrivateKey privateKey) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA384withECDSA. Tokens specify this as "ES384". + * + * @param key the key to use in the verify or signing instance. + * @return a valid ECDSA384 Algorithm. + * @throws IllegalArgumentException if the provided Key is null. + * @deprecated use {@link #ECDSA384(ECPublicKey, ECPrivateKey)} or {@link #ECDSA384(ECDSAKeyProvider)} + */ + @Deprecated + public static Algorithm ECDSA384(ECKey key) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA512withECDSA. Tokens specify this as "ES512". + * + * @param keyProvider the provider of the Public Key and Private Key for the verify and signing instance. + * @return a valid ECDSA512 Algorithm. + * @throws IllegalArgumentException if the Key Provider is null. + */ + public static Algorithm ECDSA512(ECDSAKeyProvider keyProvider) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA512withECDSA. Tokens specify this as "ES512". + * + * @param publicKey the key to use in the verify instance. + * @param privateKey the key to use in the signing instance. + * @return a valid ECDSA512 Algorithm. + * @throws IllegalArgumentException if the provided Key is null. + */ + public static Algorithm ECDSA512(ECPublicKey publicKey, ECPrivateKey privateKey) throws IllegalArgumentException { + return null; + } + + /** + * Creates a new Algorithm instance using SHA512withECDSA. Tokens specify this as "ES512". + * + * @param key the key to use in the verify or signing instance. + * @return a valid ECDSA512 Algorithm. + * @throws IllegalArgumentException if the provided Key is null. + * @deprecated use {@link #ECDSA512(ECPublicKey, ECPrivateKey)} or {@link #ECDSA512(ECDSAKeyProvider)} + */ + @Deprecated + public static Algorithm ECDSA512(ECKey key) throws IllegalArgumentException { + return null; + } + + + public static Algorithm none() { + return null; + } + + /** + * Getter for the Id of the Private Key used to sign the tokens. This is usually specified as the `kid` claim in the Header. + * + * @return the Key Id that identifies the Signing Key or null if it's not specified. + */ + public String getSigningKeyId() { + return null; + } + + /** + * Getter for the name of this Algorithm, as defined in the JWT Standard. i.e. "HS256" + * + * @return the algorithm name. + */ + public String getName() { + return null; + } + + /** + * Getter for the description of this Algorithm, required when instantiating a Mac or Signature object. i.e. "HmacSHA256" + * + * @return the algorithm description. + */ + String getDescription() { + return null; + } + + /** + * Verify the given token using this Algorithm instance. + * + * @param jwt the already decoded JWT that it's going to be verified. + * @throws SignatureVerificationException if the Token's Signature is invalid, meaning that it doesn't match the signatureBytes, or if the Key is invalid. + */ + public abstract void verify(DecodedJWT jwt) throws SignatureVerificationException; + + /** + * Sign the given content using this Algorithm instance. + * + * @param headerBytes an array of bytes representing the base64 encoded header content to be verified against the signature. + * @param payloadBytes an array of bytes representing the base64 encoded payload content to be verified against the signature. + * @return the signature in a base64 encoded array of bytes + * @throws SignatureGenerationException if the Key is invalid. + */ + public byte[] sign(byte[] headerBytes, byte[] payloadBytes) throws SignatureGenerationException { + return null; + } + + /** + * Sign the given content using this Algorithm instance. + * + * @param contentBytes an array of bytes representing the base64 encoded content to be verified against the signature. + * @return the signature in a base64 encoded array of bytes + * @throws SignatureGenerationException if the Key is invalid. + * @deprecated Please use the {@linkplain #sign(byte[], byte[])} method instead. + */ + + @Deprecated + public abstract byte[] sign(byte[] contentBytes) throws SignatureGenerationException; + +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTCreationException.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTCreationException.java new file mode 100644 index 000000000000..84644b951956 --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTCreationException.java @@ -0,0 +1,6 @@ +package com.auth0.jwt.exceptions; + +public class JWTCreationException extends RuntimeException { + public JWTCreationException(String message, Throwable cause) { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTDecodeException.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTDecodeException.java new file mode 100644 index 000000000000..1fe76bb12e5f --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTDecodeException.java @@ -0,0 +1,9 @@ +package com.auth0.jwt.exceptions; + +public class JWTDecodeException extends RuntimeException { + public JWTDecodeException(String message) { + } + + public JWTDecodeException(String message, Throwable cause) { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTVerificationException.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTVerificationException.java new file mode 100644 index 000000000000..6cb0536aa47b --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/JWTVerificationException.java @@ -0,0 +1,9 @@ +package com.auth0.jwt.exceptions; + +public class JWTVerificationException extends RuntimeException { + public JWTVerificationException(String message) { + } + + public JWTVerificationException(String message, Throwable cause) { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/SignatureGenerationException.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/SignatureGenerationException.java new file mode 100644 index 000000000000..415f8fa31890 --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/SignatureGenerationException.java @@ -0,0 +1,8 @@ +package com.auth0.jwt.exceptions; + +import com.auth0.jwt.algorithms.Algorithm; + +public class SignatureGenerationException extends RuntimeException { + public SignatureGenerationException(Algorithm algorithm, Throwable cause) { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/SignatureVerificationException.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/SignatureVerificationException.java new file mode 100644 index 000000000000..b317bd8b3ee7 --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/exceptions/SignatureVerificationException.java @@ -0,0 +1,13 @@ + + +package com.auth0.jwt.exceptions; + +import com.auth0.jwt.algorithms.Algorithm; + +public class SignatureVerificationException extends RuntimeException { + public SignatureVerificationException(Algorithm algorithm) { + } + + public SignatureVerificationException(Algorithm algorithm, Throwable cause) { + } +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/DecodedJWT.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/DecodedJWT.java new file mode 100644 index 000000000000..ba0fb21963db --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/DecodedJWT.java @@ -0,0 +1,37 @@ +package com.auth0.jwt.interfaces; + +/** + * Class that represents a Json Web Token that was decoded from it's string representation. + */ +public interface DecodedJWT { + /** + * Getter for the String Token used to create this JWT instance. + * + * @return the String Token. + */ + String getToken(); + + /** + * Getter for the Header contained in the JWT as a Base64 encoded String. + * This represents the first part of the token. + * + * @return the Header of the JWT. + */ + String getHeader(); + + /** + * Getter for the Payload contained in the JWT as a Base64 encoded String. + * This represents the second part of the token. + * + * @return the Payload of the JWT. + */ + String getPayload(); + + /** + * Getter for the Signature contained in the JWT as a Base64 encoded String. + * This represents the third part of the token. + * + * @return the Signature of the JWT. + */ + String getSignature(); +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/ECDSAKeyProvider.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/ECDSAKeyProvider.java new file mode 100644 index 000000000000..81f3382b13a9 --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/ECDSAKeyProvider.java @@ -0,0 +1,10 @@ +package com.auth0.jwt.interfaces; + +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; + +/** + * Elliptic Curve (EC) Public/Private Key provider. + */ +public interface ECDSAKeyProvider extends KeyProvider { +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/JWTVerifier.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/JWTVerifier.java new file mode 100644 index 000000000000..76a889fbe705 --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/JWTVerifier.java @@ -0,0 +1,25 @@ +package com.auth0.jwt.interfaces; + +import com.auth0.jwt.exceptions.JWTVerificationException; + + +public interface JWTVerifier { + + /** + * Performs the verification against the given Token + * + * @param token to verify. + * @return a verified and decoded JWT. + * @throws JWTVerificationException if any of the verification steps fail + */ + DecodedJWT verify(String token) throws JWTVerificationException; + + /** + * Performs the verification against the given decoded JWT + * + * @param jwt to verify. + * @return a verified and decoded JWT. + * @throws JWTVerificationException if any of the verification steps fail + */ + DecodedJWT verify(DecodedJWT jwt) throws JWTVerificationException; +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/KeyProvider.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/KeyProvider.java new file mode 100644 index 000000000000..57645efc1e19 --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/KeyProvider.java @@ -0,0 +1,35 @@ +package com.auth0.jwt.interfaces; + +import java.security.PrivateKey; +import java.security.PublicKey; + +/** + * Generic Public/Private Key provider. + * + * @param the class that represents the Public Key + * @param the class that represents the Private Key + */ +interface KeyProvider { + + /** + * Getter for the Public Key instance with the given Id. Used to verify the signature on the JWT verification stage. + * + * @param keyId the Key Id specified in the Token's Header or null if none is available. Provides a hint on which Public Key to use to verify the token's signature. + * @return the Public Key instance + */ + U getPublicKeyById(String keyId); + + /** + * Getter for the Private Key instance. Used to sign the content on the JWT signing stage. + * + * @return the Private Key instance + */ + R getPrivateKey(); + + /** + * Getter for the Id of the Private Key used to sign the tokens. This represents the `kid` claim and will be placed in the Header. + * + * @return the Key Id that identifies the Private Key or null if it's not specified. + */ + String getPrivateKeyId(); +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/RSAKeyProvider.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/RSAKeyProvider.java new file mode 100644 index 000000000000..215744c6d68c --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/RSAKeyProvider.java @@ -0,0 +1,10 @@ +package com.auth0.jwt.interfaces; + +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +/** + * RSA Public/Private Key provider. + */ +public interface RSAKeyProvider extends KeyProvider { +} \ No newline at end of file diff --git a/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/Verification.java b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/Verification.java new file mode 100644 index 000000000000..8567d24498a2 --- /dev/null +++ b/java/ql/test/stubs/auth0-jwt-2.3/com/auth0/jwt/interfaces/Verification.java @@ -0,0 +1,189 @@ +package com.auth0.jwt.interfaces; + +import java.util.Date; + +/** + * Holds the Claims and claim-based configurations required for a JWT to be considered valid. + */ +public interface Verification { + /** + * Require a specific Issuer ("iss") claim. + * + * @param issuer the required Issuer value. If multiple values are given, the claim must at least match one of them + * @return this same Verification instance. + */ + Verification withIssuer(String... issuer); + + /** + * Require a specific Subject ("sub") claim. + * + * @param subject the required Subject value + * @return this same Verification instance. + */ + Verification withSubject(String subject); + + /** + * Require a specific Audience ("aud") claim. If multiple audiences are specified, they must all be present + * in the "aud" claim. + * + * If this is used in conjunction with {@link #withAnyOfAudience(String...)}, whichever one is configured last will + * determine the audience validation behavior. + * + * @param audience the required Audience value + * @return this same Verification instance. + */ + Verification withAudience(String... audience); + + /** + * Set a specific leeway window in seconds in which the Expires At ("exp") Claim will still be valid. + * Expiration Date is always verified when the value is present. This method overrides the value set with acceptLeeway + * + * @param leeway the window in seconds in which the Expires At Claim will still be valid. + * @return this same Verification instance. + * @throws IllegalArgumentException if leeway is negative. + */ + Verification acceptExpiresAt(long leeway) throws IllegalArgumentException; + + /** + * Set a specific leeway window in seconds in which the Not Before ("nbf") Claim will still be valid. + * Not Before Date is always verified when the value is present. This method overrides the value set with acceptLeeway + * + * @param leeway the window in seconds in which the Not Before Claim will still be valid. + * @return this same Verification instance. + * @throws IllegalArgumentException if leeway is negative. + */ + Verification acceptNotBefore(long leeway) throws IllegalArgumentException; + + /** + * Set a specific leeway window in seconds in which the Issued At ("iat") Claim will still be valid. + * This method overrides the value set with {@link #acceptLeeway(long)}. + * By default, the Issued At claim is always verified when the value is present, unless disabled with {@link #ignoreIssuedAt()}. + * If Issued At verification has been disabled, no verification of the Issued At claim will be performed, and this method has no effect. + * + * @param leeway the window in seconds in which the Issued At Claim will still be valid. + * @return this same Verification instance. + * @throws IllegalArgumentException if leeway is negative. + */ + Verification acceptIssuedAt(long leeway) throws IllegalArgumentException; + + /** + * Require a specific JWT Id ("jti") claim. + * + * @param jwtId the required Id value + * @return this same Verification instance. + */ + Verification withJWTId(String jwtId); + + /** + * Require a claim to be present, with any value. + * @param name the Claim's name. + * @return this same Verification instance + * @throws IllegalArgumentException if the name is null. + */ + Verification withClaimPresence(String name) throws IllegalArgumentException; + + /** + * Require a specific Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Verification instance. + * @throws IllegalArgumentException if the name is null. + */ + Verification withClaim(String name, Boolean value) throws IllegalArgumentException; + + /** + * Require a specific Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Verification instance. + * @throws IllegalArgumentException if the name is null. + */ + Verification withClaim(String name, Integer value) throws IllegalArgumentException; + + /** + * Require a specific Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Verification instance. + * @throws IllegalArgumentException if the name is null. + */ + Verification withClaim(String name, Long value) throws IllegalArgumentException; + + /** + * Require a specific Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Verification instance. + * @throws IllegalArgumentException if the name is null. + */ + Verification withClaim(String name, Double value) throws IllegalArgumentException; + + /** + * Require a specific Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Verification instance. + * @throws IllegalArgumentException if the name is null. + */ + Verification withClaim(String name, String value) throws IllegalArgumentException; + + /** + * Require a specific Claim value. + * + * @param name the Claim's name. + * @param value the Claim's value. + * @return this same Verification instance. + * @throws IllegalArgumentException if the name is null. + */ + Verification withClaim(String name, Date value) throws IllegalArgumentException; + + /** + * Require a specific Array Claim to contain at least the given items. + * + * @param name the Claim's name. + * @param items the items the Claim must contain. + * @return this same Verification instance. + * @throws IllegalArgumentException if the name is null. + */ + Verification withArrayClaim(String name, String... items) throws IllegalArgumentException; + + /** + * Require a specific Array Claim to contain at least the given items. + * + * @param name the Claim's name. + * @param items the items the Claim must contain. + * @return this same Verification instance. + * @throws IllegalArgumentException if the name is null. + */ + Verification withArrayClaim(String name, Integer... items) throws IllegalArgumentException; + + /** + * Require a specific Array Claim to contain at least the given items. + * + * @param name the Claim's name. + * @param items the items the Claim must contain. + * @return this same Verification instance. + * @throws IllegalArgumentException if the name is null. + */ + + Verification withArrayClaim(String name, Long ... items) throws IllegalArgumentException; + + /** + * Skip the Issued At ("iat") date verification. By default, the verification is performed. + * + * @return this same Verification instance. + */ + Verification ignoreIssuedAt(); + + /** + * Creates a new and reusable instance of the JWTVerifier with the configuration already provided. + * + * @return a new JWTVerifier instance. + */ + JWTVerifier build(); +} \ No newline at end of file From 75e7148912e3e70f322cced85d49319adc6f4a60 Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Mon, 9 May 2022 16:10:11 +0000 Subject: [PATCH 2/4] Standardize the query and update qldoc --- .../Security/CWE/CWE-321/HardcodedJwtKey.qll | 43 +++++++------------ 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll index c960b065215d..137f5dd566ed 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll @@ -3,25 +3,26 @@ */ import java +private import semmle.code.java.dataflow.ExternalFlow private import semmle.code.java.dataflow.FlowSources -/** The Java class `com.auth0.jwt.JWT`. */ +/** The class `com.auth0.jwt.JWT`. */ class Jwt extends RefType { Jwt() { this.hasQualifiedName("com.auth0.jwt", "JWT") } } -/** The Java class `com.auth0.jwt.JWTCreator.Builder`. */ +/** The class `com.auth0.jwt.JWTCreator.Builder`. */ class JwtBuilder extends RefType { JwtBuilder() { this.hasQualifiedName("com.auth0.jwt", "JWTCreator$Builder") } } -/** The Java class `com.auth0.jwt.algorithms.Algorithm`. */ -class Algorithm extends RefType { - Algorithm() { this.hasQualifiedName("com.auth0.jwt.algorithms", "Algorithm") } +/** The class `com.auth0.jwt.algorithms.Algorithm`. */ +class JwtAlgorithm extends RefType { + JwtAlgorithm() { this.hasQualifiedName("com.auth0.jwt.algorithms", "Algorithm") } } /** - * The Java interface `com.auth0.jwt.interfaces.JWTVerifier` or it implementation class + * The interface `com.auth0.jwt.interfaces.JWTVerifier` or its implementation * `com.auth0.jwt.JWTVerifier`. */ class JwtVerifier extends RefType { @@ -30,15 +31,11 @@ class JwtVerifier extends RefType { } } -/** The secret generation method declared in `com.auth0.jwt.algorithms.Algorithm`. */ -class GetSecretMethod extends Method { - GetSecretMethod() { - this.getDeclaringType() instanceof Algorithm and - ( - this.getName().substring(0, 4) = "HMAC" or - this.getName().substring(0, 5) = "ECDSA" or - this.getName().substring(0, 3) = "RSA" - ) +/** A method that creates an instance of `com.auth0.jwt.algorithms.Algorithm`. */ +class GetAlgorithmMethod extends Method { + GetAlgorithmMethod() { + this.getDeclaringType() instanceof JwtAlgorithm and + this.getName().matches(["HMAC%", "ECDSA%", "RSA%"]) } } @@ -76,19 +73,11 @@ abstract class JwtKeySource extends DataFlow::Node { } */ abstract class JwtTokenSink extends DataFlow::Node { } -private predicate isTestCode(Expr e) { - e.getFile().getAbsolutePath().toLowerCase().matches("%test%") and - not e.getFile().getAbsolutePath().toLowerCase().matches("%ql/test%") -} - /** * A hardcoded string literal as a source for JWT token signing vulnerabilities. */ class HardcodedKeyStringSource extends JwtKeySource { - HardcodedKeyStringSource() { - this.asExpr() instanceof CompileTimeConstantExpr and - not isTestCode(this.asExpr()) - } + HardcodedKeyStringSource() { this.asExpr() instanceof CompileTimeConstantExpr } } /** @@ -128,7 +117,7 @@ class HardcodedJwtKeyConfiguration extends TaintTracking::Configuration { override predicate isAdditionalTaintStep(DataFlow::Node prev, DataFlow::Node succ) { exists(MethodAccess ma | ( - ma.getMethod() instanceof GetSecretMethod or + ma.getMethod() instanceof GetAlgorithmMethod or ma.getMethod() instanceof RequireMethod ) and prev.asExpr() = ma.getArgument(0) and @@ -145,12 +134,12 @@ private class VerificationFlowStep extends SummaryModelCsv { "com.auth0.jwt.interfaces;Verification;true;build;;;Argument[-1];ReturnValue;taint", "com.auth0.jwt.interfaces;Verification;true;" + ["acceptLeeway", "acceptExpiresAt", "acceptNotBefore", "acceptIssuedAt", "ignoreIssuedAt"] - + ";;;Argument[-1];ReturnValue;taint", + + ";;;Argument[-1];ReturnValue;value", "com.auth0.jwt.interfaces;Verification;true;with" + [ "Issuer", "Subject", "Audience", "AnyOfAudience", "ClaimPresence", "Claim", "ArrayClaim", "JWTId" - ] + ";;;Argument[-1];ReturnValue;taint" + ] + ";;;Argument[-1];ReturnValue;value" ] } } From f85c01c97555ece8f184203fb7c3f20cfb13687e Mon Sep 17 00:00:00 2001 From: luchua-bc Date: Wed, 11 May 2022 10:37:22 +0000 Subject: [PATCH 3/4] Correct string source --- .../src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll index 137f5dd566ed..0a92e6b141a6 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qll @@ -77,7 +77,7 @@ abstract class JwtTokenSink extends DataFlow::Node { } * A hardcoded string literal as a source for JWT token signing vulnerabilities. */ class HardcodedKeyStringSource extends JwtKeySource { - HardcodedKeyStringSource() { this.asExpr() instanceof CompileTimeConstantExpr } + HardcodedKeyStringSource() { exists(this.asExpr().(CompileTimeConstantExpr).getStringValue()) } } /** From f7e1f3e1a51c229e8399144519174579dfe39dfe Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Wed, 11 May 2022 14:38:09 +0100 Subject: [PATCH 4/4] Remove URL fragment from Google search --- .../experimental/Security/CWE/CWE-321/HardcodedJwtKey.qhelp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qhelp b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qhelp index b8e984280be4..e6a25a3b96c7 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qhelp +++ b/java/ql/src/experimental/Security/CWE/CWE-321/HardcodedJwtKey.qhelp @@ -35,7 +35,7 @@

  • Semgrep Blog: - Hardcoded secrets, unverified tokens, and other common JWT mistakes + Hardcoded secrets, unverified tokens, and other common JWT mistakes
  • CVE-2022-24860: @@ -43,4 +43,4 @@
  • - \ No newline at end of file +