|
| 1 | +/** |
| 2 | + * Finds calls to `assertThrows` and `assertThrowsExactly` where a 'message' argument is provided which |
| 3 | + * seems to be intended as 'expected message'. The 'message' is actually what will be used in case the |
| 4 | + * assertion fails, it is _not_ the expected message for the thrown exception. |
| 5 | + * |
| 6 | + * For example this assertion will pass even though the exception has a different message: |
| 7 | + * ```java |
| 8 | + * assertThrows( |
| 9 | + * IllegalArgumentException.class, |
| 10 | + * () -> { |
| 11 | + * throw new IllegalArgumentException("argument 'actual' is invalid"); |
| 12 | + * }, |
| 13 | + * "argument 'expected' is invalid" |
| 14 | + * ); |
| 15 | + * ``` |
| 16 | + * |
| 17 | + * @id todo |
| 18 | + * @kind problem |
| 19 | + */ |
| 20 | + |
| 21 | +import java |
| 22 | + |
| 23 | +from |
| 24 | + MethodAccess assertThrowsCall, Method assertThrowsMethod, Class expectedException, |
| 25 | + CompileTimeConstantExpr messageArg |
| 26 | +where |
| 27 | + assertThrowsCall.getMethod() = assertThrowsMethod and |
| 28 | + // TODO: Use own assertion lib CodeQL classes? |
| 29 | + ( |
| 30 | + // JUnit 4 |
| 31 | + assertThrowsMethod.getDeclaringType().hasQualifiedName("org.junit", "Assert") and |
| 32 | + assertThrowsMethod.hasName("assertThrows") and |
| 33 | + expectedException = assertThrowsCall.getArgument(1).(TypeLiteral).getReferencedType() and |
| 34 | + messageArg = assertThrowsCall.getArgument(0) |
| 35 | + or |
| 36 | + // JUnit 5 |
| 37 | + assertThrowsMethod.getDeclaringType().hasQualifiedName("org.junit.jupiter.api", "Assertions") and |
| 38 | + assertThrowsMethod.hasName(["assertThrows", "assertThrowsExactly"]) and |
| 39 | + expectedException = assertThrowsCall.getArgument(0).(TypeLiteral).getReferencedType() and |
| 40 | + messageArg = assertThrowsCall.getArgument(2) |
| 41 | + ) and |
| 42 | + // Check if anywhere in the code an exception with the exact same message is created |
| 43 | + exists(ConstructorCall newExceptionCall | |
| 44 | + newExceptionCall.getConstructedType().getASourceSupertype*() = expectedException and |
| 45 | + newExceptionCall.getAnArgument().(CompileTimeConstantExpr).getStringValue() = |
| 46 | + messageArg.getStringValue() |
| 47 | + ) |
| 48 | +select messageArg, "Message argument of `assertThrows` misunderstood as 'expected message'" |
0 commit comments