mirror of
https://github.com/github/codeql.git
synced 2026-04-25 08:45:14 +02:00
Merge branch 'main' into signature_model_refactor
This commit is contained in:
@@ -85,7 +85,7 @@ java.applet,,,11,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,11,
|
||||
java.awt,1,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,3
|
||||
java.beans,1,,177,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,82,95
|
||||
java.io,66,1,225,,,,,,,,,22,,,,,,,,,,,,,,,44,,,,,,,,,,,,,,,,,,,,,,,1,,202,23
|
||||
java.lang,38,3,783,,13,,,,,,1,,,,,,,,,,,,8,,,,11,,,4,,,1,,,,,,,,,,,,,,,,3,,,506,277
|
||||
java.lang,38,3,790,,13,,,,,,1,,,,,,,,,,,,8,,,,11,,,4,,,1,,,,,,,,,,,,,,,,3,,,510,280
|
||||
java.math,,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9
|
||||
java.net,23,3,347,,,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,21,,,,,,,,,,,,,,,3,248,99
|
||||
java.nio,47,,499,,,,,,,,,5,,,,,,,,,,,,,,,41,,,,,,,,,1,,,,,,,,,,,,,,,,302,197
|
||||
@@ -99,7 +99,7 @@ javafx.scene.web,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,
|
||||
javax.accessibility,,,63,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,28,35
|
||||
javax.activation,2,,7,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,1,,,,,,,,,,,,,,,,7,
|
||||
javax.annotation.processing,,,28,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,25,3
|
||||
javax.crypto,19,,114,,,12,3,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,61,53
|
||||
javax.crypto,19,,140,,,12,3,,2,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,76,64
|
||||
javax.faces.context,4,7,,,,,,,,,,,,,,2,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,7,,
|
||||
javax.imageio,1,,304,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,,138,166
|
||||
javax.jms,,9,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,57,
|
||||
|
||||
|
@@ -18,10 +18,10 @@ Java framework & library support
|
||||
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,730,43,9,,,,,
|
||||
JBoss Logging,``org.jboss.logging``,,,324,,,,,,
|
||||
`JSON-java <https://github.com/stleary/JSON-java>`_,``org.json``,,236,,,,,,,
|
||||
Java Standard Library,``java.*``,10,4621,260,99,,9,,,26
|
||||
Java extensions,"``javax.*``, ``jakarta.*``",87,4159,90,10,4,2,1,1,4
|
||||
Java Standard Library,``java.*``,10,4628,260,99,,9,,,26
|
||||
Java extensions,"``javax.*``, ``jakarta.*``",87,4185,90,10,4,2,1,1,4
|
||||
Kotlin Standard Library,``kotlin*``,,1849,16,14,,,,,2
|
||||
`Spring <https://spring.io/>`_,``org.springframework.*``,38,486,143,26,,28,14,,35
|
||||
Others,"``actions.osgi``, ``antlr``, ``ch.ethz.ssh2``, ``cn.hutool.core.codec``, ``com.alibaba.com.caucho.hessian.io``, ``com.alibaba.druid.sql``, ``com.alibaba.fastjson2``, ``com.amazonaws.auth``, ``com.auth0.jwt.algorithms``, ``com.azure.identity``, ``com.caucho.burlap.io``, ``com.caucho.hessian.io``, ``com.cedarsoftware.util.io``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.esotericsoftware.yamlbeans``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.microsoft.sqlserver.jdbc``, ``com.mitchellbosecke.pebble``, ``com.mongodb``, ``com.opensymphony.xwork2``, ``com.rabbitmq.client``, ``com.sshtools.j2ssh.authentication``, ``com.sun.crypto.provider``, ``com.sun.jndi.ldap``, ``com.sun.net.httpserver``, ``com.sun.net.ssl``, ``com.sun.rowset``, ``com.sun.security.auth.module``, ``com.sun.security.ntlm``, ``com.sun.security.sasl.digest``, ``com.thoughtworks.xstream``, ``com.trilead.ssh2``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``io.undertow.server.handlers.resource``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``liquibase.database.jvm``, ``liquibase.statement.core``, ``net.lingala.zip4j``, ``net.schmizz.sshj``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.cxf.catalog``, ``org.apache.cxf.common.classloader``, ``org.apache.cxf.common.jaxb``, ``org.apache.cxf.common.logging``, ``org.apache.cxf.configuration.jsse``, ``org.apache.cxf.helpers``, ``org.apache.cxf.resource``, ``org.apache.cxf.staxutils``, ``org.apache.cxf.tools.corba.utils``, ``org.apache.cxf.tools.util``, ``org.apache.cxf.transform``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hadoop.hive.ql.exec``, ``org.apache.hadoop.hive.ql.metadata``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.ibatis.mapping``, ``org.apache.log4j``, ``org.apache.shiro.authc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.shiro.mgt``, ``org.apache.sshd.client.session``, ``org.apache.struts.beanvalidation.validation.interceptor``, ``org.apache.struts2``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.exolab.castor.xml``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.hibernate``, ``org.ho.yaml``, ``org.influxdb``, ``org.jabsorb``, ``org.jboss.vfs``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.keycloak.models.map.storage``, ``org.kohsuke.stapler``, ``org.lastaflute.web``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.owasp.esapi``, ``org.pac4j.jwt.config.encryption``, ``org.pac4j.jwt.config.signature``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``, ``software.amazon.awssdk.transfer.s3.model``, ``sun.jvmstat.perfdata.monitor.protocol.local``, ``sun.jvmstat.perfdata.monitor.protocol.rmi``, ``sun.misc``, ``sun.net.ftp``, ``sun.net.www.protocol.http``, ``sun.security.acl``, ``sun.security.jgss.krb5``, ``sun.security.krb5``, ``sun.security.pkcs``, ``sun.security.pkcs11``, ``sun.security.provider``, ``sun.security.ssl``, ``sun.security.x509``, ``sun.tools.jconsole``",133,10525,927,140,6,22,18,,208
|
||||
Totals,,330,26328,2656,404,16,128,33,1,409
|
||||
Totals,,330,26361,2656,404,16,128,33,1,409
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
description: Remove Java 25 compact source files support by removing `isImplicitClass` table
|
||||
compatibility: partial
|
||||
isImplicitClass.rel: delete
|
||||
@@ -1,6 +1,6 @@
|
||||
diagnosticAttributes
|
||||
| Scanning Java code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code, among other reasons -- see other CodeQL diagnostics reported on the CodeQL status page for more details of possible causes. Addressing these warnings is advisable to avoid false-positive or missing results. If they cannot be addressed, consider scanning Java using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityCliSummaryTable | true |
|
||||
| Scanning Java code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code, among other reasons -- see other CodeQL diagnostics reported on the CodeQL status page for more details of possible causes. Addressing these warnings is advisable to avoid false-positive or missing results. If they cannot be addressed, consider scanning Java using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityStatusPage | true |
|
||||
| Scanning Java code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code, among other reasons -- see other CodeQL diagnostics reported on the CodeQL status page for more details of possible causes. Addressing these warnings is advisable to avoid false-positive or missing results. If they cannot be addressed, consider scanning Java using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityTelemetry | true |
|
||||
| Scanning Java code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 20 % (threshold 85 %). Percentage of expressions with known type: 14 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning Java using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityCliSummaryTable | true |
|
||||
| Scanning Java code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 20 % (threshold 85 %). Percentage of expressions with known type: 14 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning Java using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityStatusPage | true |
|
||||
| Scanning Java code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 20 % (threshold 85 %). Percentage of expressions with known type: 14 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning Java using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | visibilityTelemetry | true |
|
||||
#select
|
||||
| Scanning Java code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code, among other reasons -- see other CodeQL diagnostics reported on the CodeQL status page for more details of possible causes. Addressing these warnings is advisable to avoid false-positive or missing results. If they cannot be addressed, consider scanning Java using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | Scanning Java code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code, among other reasons -- see other CodeQL diagnostics reported on the CodeQL status page for more details of possible causes. Addressing these warnings is advisable to avoid false-positive or missing results. If they cannot be addressed, consider scanning Java using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | 1 |
|
||||
| Scanning Java code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 20 % (threshold 85 %). Percentage of expressions with known type: 14 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning Java using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | Scanning Java code completed successfully, but the scan encountered issues. This may be caused by problems identifying dependencies or use of generated source code. Some metrics of the database quality are: Percentage of calls with call target: 20 % (threshold 85 %). Percentage of expressions with known type: 14 % (threshold 85 %). Ideally these metrics should be above their thresholds. Addressing these issues is advisable to avoid false-positives or missing results. If they cannot be addressed, consider scanning Java using either the `autobuild` or `manual` [build modes](https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages#comparison-of-the-build-modes). | 1 |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
| Annotation processors enabled: true | 1 |
|
||||
| Annotation processors enabled: false | 1 |
|
||||
| Number of calls with call target | 1 |
|
||||
| Number of calls with missing call target | 4 |
|
||||
| Number of diagnostics from CodeQL Java extractor with severity 5 | 10 |
|
||||
@@ -15,5 +15,3 @@
|
||||
| Total number of diagnostics from CodeQL Java extractor | 12 |
|
||||
| Total number of lines | 13 |
|
||||
| Total number of lines with extension java | 13 |
|
||||
| Used annotation processor: lombok.launch.AnnotationProcessorHider$AnnotationProcessor | 1 |
|
||||
| Used annotation processor: lombok.launch.AnnotationProcessorHider$ClaimingProcessor | 1 |
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
query: Likely Bugs/Arithmetic/ConstantExpAppearsNonConstant.ql
|
||||
postprocess: utils/test/InlineExpectationsTestQuery.ql
|
||||
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
public static void updateFlashlights(Minecraft mc){
|
||||
if(mc.world != null){
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
def test(codeql, java):
|
||||
codeql.database.create(build_mode="none")
|
||||
@@ -1,61 +1,117 @@
|
||||
ql/java/ql/src/Advisory/Declarations/MissingOverrideAnnotation.ql
|
||||
ql/java/ql/src/Advisory/Declarations/NonFinalImmutableField.ql
|
||||
ql/java/ql/src/Advisory/Declarations/NonPrivateField.ql
|
||||
ql/java/ql/src/Advisory/Documentation/ImpossibleJavadocThrows.ql
|
||||
ql/java/ql/src/Advisory/Documentation/SpuriousJavadocParam.ql
|
||||
ql/java/ql/src/Advisory/Java Objects/AvoidCloneMethodAccess.ql
|
||||
ql/java/ql/src/Advisory/Java Objects/AvoidCloneOverride.ql
|
||||
ql/java/ql/src/Advisory/Java Objects/AvoidCloneableInterface.ql
|
||||
ql/java/ql/src/Advisory/Java Objects/AvoidFinalizeOverride.ql
|
||||
ql/java/ql/src/Advisory/Naming/NamingConventionsConstants.ql
|
||||
ql/java/ql/src/Advisory/Naming/NamingConventionsMethods.ql
|
||||
ql/java/ql/src/Advisory/Naming/NamingConventionsPackages.ql
|
||||
ql/java/ql/src/Advisory/Naming/NamingConventionsRefTypes.ql
|
||||
ql/java/ql/src/Advisory/Naming/NamingConventionsVariables.ql
|
||||
ql/java/ql/src/Advisory/Statements/MissingDefaultInSwitch.ql
|
||||
ql/java/ql/src/Advisory/Statements/OneStatementPerLine.ql
|
||||
ql/java/ql/src/Advisory/Statements/TerminateIfElseIfWithElse.ql
|
||||
ql/java/ql/src/Advisory/Types/GenericsConstructor.ql
|
||||
ql/java/ql/src/Advisory/Types/GenericsReturnType.ql
|
||||
ql/java/ql/src/Advisory/Types/GenericsVariable.ql
|
||||
ql/java/ql/src/Compatibility/JDK9/JdkInternalAccess.ql
|
||||
ql/java/ql/src/Compatibility/JDK9/UnderscoreIdentifier.ql
|
||||
ql/java/ql/src/DeadCode/DeadClass.ql
|
||||
ql/java/ql/src/DeadCode/DeadEnumConstant.ql
|
||||
ql/java/ql/src/DeadCode/DeadField.ql
|
||||
ql/java/ql/src/DeadCode/DeadMethod.ql
|
||||
ql/java/ql/src/DeadCode/UselessParameter.ql
|
||||
ql/java/ql/src/Language Abuse/EmptyMethod.ql
|
||||
ql/java/ql/src/Language Abuse/IterableIterator.ql
|
||||
ql/java/ql/src/Language Abuse/LabelInSwitch.ql
|
||||
ql/java/ql/src/Language Abuse/OverridePackagePrivate.ql
|
||||
ql/java/ql/src/Language Abuse/TypeVarExtendsFinalType.ql
|
||||
ql/java/ql/src/Language Abuse/TypeVariableHidesType.ql
|
||||
ql/java/ql/src/Language Abuse/UselessNullCheck.ql
|
||||
ql/java/ql/src/Language Abuse/UselessTypeTest.ql
|
||||
ql/java/ql/src/Language Abuse/WrappedIterator.ql
|
||||
ql/java/ql/src/Likely Bugs/Arithmetic/BadAbsOfRandom.ql
|
||||
ql/java/ql/src/Likely Bugs/Arithmetic/ConstantExpAppearsNonConstant.ql
|
||||
ql/java/ql/src/Likely Bugs/Arithmetic/IntMultToLong.ql
|
||||
ql/java/ql/src/Likely Bugs/Arithmetic/LShiftLargerThanTypeWidth.ql
|
||||
ql/java/ql/src/Likely Bugs/Arithmetic/MultiplyRemainder.ql
|
||||
ql/java/ql/src/Likely Bugs/Arithmetic/RandomUsedOnce.ql
|
||||
ql/java/ql/src/Likely Bugs/Arithmetic/WhitespaceContradictsPrecedence.ql
|
||||
ql/java/ql/src/Likely Bugs/Cloning/MissingCallToSuperClone.ql
|
||||
ql/java/ql/src/Likely Bugs/Cloning/MissingMethodClone.ql
|
||||
ql/java/ql/src/Likely Bugs/Collections/ArrayIndexOutOfBounds.ql
|
||||
ql/java/ql/src/Likely Bugs/Collections/ContainsTypeMismatch.ql
|
||||
ql/java/ql/src/Likely Bugs/Collections/IteratorRemoveMayFail.ql
|
||||
ql/java/ql/src/Likely Bugs/Collections/ReadOnlyContainer.ql
|
||||
ql/java/ql/src/Likely Bugs/Collections/RemoveTypeMismatch.ql
|
||||
ql/java/ql/src/Likely Bugs/Collections/WriteOnlyContainer.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/CompareIdenticalValues.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/CovariantCompareTo.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/CovariantEquals.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/EqualsArray.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/HashedButNoHash.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/IncomparableEquals.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/InconsistentCompareTo.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/InconsistentEqualsHashCode.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/MissingInstanceofInEquals.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/RefEqBoxed.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/StringComparison.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/UselessComparisonTest.ql
|
||||
ql/java/ql/src/Likely Bugs/Comparison/WrongNanComparison.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/CallsToConditionWait.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/CallsToRunnableRun.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/DateFormatThreadUnsafe.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/DoubleCheckedLocking.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/DoubleCheckedLockingWithInitRace.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/FutileSynchOnField.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/NonSynchronizedOverride.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/NotifyNotNotifyAll.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/ScheduledThreadPoolExecutorZeroThread.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/SleepWithLock.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/StartInConstructor.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/SynchOnBoxedType.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/SynchSetUnsynchGet.ql
|
||||
ql/java/ql/src/Likely Bugs/Concurrency/SynchWriteObject.ql
|
||||
ql/java/ql/src/Likely Bugs/Finalization/NullifiedSuperFinalize.ql
|
||||
ql/java/ql/src/Likely Bugs/Frameworks/JUnit/BadSuiteMethod.ql
|
||||
ql/java/ql/src/Likely Bugs/Frameworks/JUnit/JUnit5MissingNestedAnnotation.ql
|
||||
ql/java/ql/src/Likely Bugs/Frameworks/Swing/BadlyOverriddenAdapter.ql
|
||||
ql/java/ql/src/Likely Bugs/Inheritance/NoNonFinalInConstructor.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/ContainerSizeCmpZero.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/ContradictoryTypeChecks.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/DangerousNonCircuitLogic.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/EqualsTypo.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/HashCodeTypo.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/MissingFormatArg.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/MissingSpaceTypo.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/SelfAssignment.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/StringBufferCharInit.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/SuspiciousDateFormat.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/ToStringTypo.ql
|
||||
ql/java/ql/src/Likely Bugs/Likely Typos/UnusedFormatArg.ql
|
||||
ql/java/ql/src/Likely Bugs/Nullness/NullAlways.ql
|
||||
ql/java/ql/src/Likely Bugs/Nullness/NullExprDeref.ql
|
||||
ql/java/ql/src/Likely Bugs/Nullness/NullMaybe.ql
|
||||
ql/java/ql/src/Likely Bugs/Reflection/AnnotationPresentCheck.ql
|
||||
ql/java/ql/src/Likely Bugs/Resource Leaks/CloseReader.ql
|
||||
ql/java/ql/src/Likely Bugs/Resource Leaks/CloseSql.ql
|
||||
ql/java/ql/src/Likely Bugs/Resource Leaks/CloseWriter.ql
|
||||
ql/java/ql/src/Likely Bugs/Serialization/IncorrectSerialVersionUID.ql
|
||||
ql/java/ql/src/Likely Bugs/Serialization/IncorrectSerializableMethods.ql
|
||||
ql/java/ql/src/Likely Bugs/Serialization/MissingVoidConstructorOnExternalizable.ql
|
||||
ql/java/ql/src/Likely Bugs/Serialization/MissingVoidConstructorsOnSerializable.ql
|
||||
ql/java/ql/src/Likely Bugs/Serialization/NonSerializableInnerClass.ql
|
||||
ql/java/ql/src/Likely Bugs/Serialization/ReadResolveObject.ql
|
||||
ql/java/ql/src/Likely Bugs/Statements/ContinueInFalseLoop.ql
|
||||
ql/java/ql/src/Likely Bugs/Statements/MissingEnumInSwitch.ql
|
||||
ql/java/ql/src/Likely Bugs/Statements/PartiallyMaskedCatch.ql
|
||||
ql/java/ql/src/Likely Bugs/Statements/UseBraces.ql
|
||||
ql/java/ql/src/Likely Bugs/Termination/ConstantLoopCondition.ql
|
||||
ql/java/ql/src/Likely Bugs/Termination/SpinOnField.ql
|
||||
ql/java/ql/src/Performance/InefficientEmptyStringTest.ql
|
||||
ql/java/ql/src/Performance/InefficientKeySetIterator.ql
|
||||
ql/java/ql/src/Performance/InefficientOutputStream.ql
|
||||
@@ -64,6 +120,7 @@ ql/java/ql/src/Performance/InnerClassCouldBeStatic.ql
|
||||
ql/java/ql/src/Performance/NewStringString.ql
|
||||
ql/java/ql/src/Performance/StringReplaceAllWithNonRegex.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Boxed Types/BoxedVariable.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Dead Code/CreatesEmptyZip.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Dead Code/DeadRefTypes.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Dead Code/InterfaceCannotBeImplemented.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Dead Code/UnreadLocal.ql
|
||||
@@ -73,14 +130,21 @@ ql/java/ql/src/Violations of Best Practice/Exception Handling/IgnoreExceptionalR
|
||||
ql/java/ql/src/Violations of Best Practice/Exception Handling/NumberFormatException.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Implementation Hiding/AbstractToConcreteCollection.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Implementation Hiding/ExposeRepresentation.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Implementation Hiding/GetClassGetResource.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Implementation Hiding/VisibleForTestingAbuse.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/AmbiguousOuterSuper.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingMethodNames.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/FieldMasksSuperField.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/LocalShadowsFieldConfusing.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Records/IgnoredSerializationMembersOfRecordClass.ql
|
||||
ql/java/ql/src/Violations of Best Practice/SpecialCharactersInLiterals/NonExplicitControlAndWhitespaceCharsInLiterals.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Testing/ExcessivePublicMethodMocking.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToRunFinalizersOnExit.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToSystemExit.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DoNotCallFinalize.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/NextFromIterator.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/PrintLnArray.ql
|
||||
|
||||
@@ -71,6 +71,7 @@ ql/java/ql/src/Violations of Best Practice/Exception Handling/IgnoreExceptionalR
|
||||
ql/java/ql/src/Violations of Best Practice/Exception Handling/NumberFormatException.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Implementation Hiding/AbstractToConcreteCollection.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Implementation Hiding/ExposeRepresentation.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Implementation Hiding/VisibleForTestingAbuse.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/AmbiguousOuterSuper.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingMethodNames.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql
|
||||
@@ -78,6 +79,7 @@ ql/java/ql/src/Violations of Best Practice/Naming Conventions/LocalShadowsFieldC
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Records/IgnoredSerializationMembersOfRecordClass.ql
|
||||
ql/java/ql/src/Violations of Best Practice/SpecialCharactersInLiterals/NonExplicitControlAndWhitespaceCharsInLiterals.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Testing/ExcessivePublicMethodMocking.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DoNotCallFinalize.ql
|
||||
|
||||
@@ -27,6 +27,7 @@ ql/java/ql/src/Security/CWE/CWE-113/ResponseSplitting.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-1204/StaticInitializationVector.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-134/ExternallyControlledFormatString.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuatorsConfig/SpringBootActuatorsConfig.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/SensitiveDataExposureThroughErrorMessage.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-266/IntentUriPermissionManipulation.ql
|
||||
|
||||
@@ -143,6 +143,7 @@ ql/java/ql/src/Security/CWE/CWE-200/AndroidSensitiveTextField.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsAllowsContentAccess.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuatorsConfig/SpringBootActuatorsConfig.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/TempDirLocalInformationDisclosure.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/SensitiveDataExposureThroughErrorMessage.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
|
||||
|
||||
@@ -46,6 +46,7 @@ ql/java/ql/src/Security/CWE/CWE-200/AndroidSensitiveTextField.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsAllowsContentAccess.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuatorsConfig/SpringBootActuatorsConfig.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/TempDirLocalInformationDisclosure.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/SensitiveDataExposureThroughErrorMessage.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
ql/java/ql/src/Advisory/Declarations/NonFinalImmutableField.ql
|
||||
ql/java/ql/src/Advisory/Declarations/NonPrivateField.ql
|
||||
ql/java/ql/src/Advisory/Documentation/MissingJavadocMethods.ql
|
||||
ql/java/ql/src/Advisory/Documentation/MissingJavadocParameters.ql
|
||||
ql/java/ql/src/Advisory/Documentation/MissingJavadocReturnValues.ql
|
||||
ql/java/ql/src/Advisory/Documentation/MissingJavadocThrows.ql
|
||||
ql/java/ql/src/Advisory/Documentation/MissingJavadocTypes.ql
|
||||
ql/java/ql/src/Advisory/Java Objects/AvoidCloneMethodAccess.ql
|
||||
ql/java/ql/src/Advisory/Java Objects/AvoidCloneOverride.ql
|
||||
ql/java/ql/src/Advisory/Java Objects/AvoidCloneableInterface.ql
|
||||
ql/java/ql/src/Advisory/Java Objects/AvoidFinalizeOverride.ql
|
||||
ql/java/ql/src/Advisory/Naming/NamingConventionsConstants.ql
|
||||
ql/java/ql/src/Advisory/Naming/NamingConventionsMethods.ql
|
||||
ql/java/ql/src/Advisory/Naming/NamingConventionsPackages.ql
|
||||
ql/java/ql/src/Advisory/Naming/NamingConventionsRefTypes.ql
|
||||
ql/java/ql/src/Advisory/Naming/NamingConventionsVariables.ql
|
||||
ql/java/ql/src/Advisory/Statements/MissingDefaultInSwitch.ql
|
||||
ql/java/ql/src/Advisory/Statements/OneStatementPerLine.ql
|
||||
ql/java/ql/src/Advisory/Statements/TerminateIfElseIfWithElse.ql
|
||||
ql/java/ql/src/Advisory/Types/GenericsConstructor.ql
|
||||
ql/java/ql/src/Advisory/Types/GenericsReturnType.ql
|
||||
ql/java/ql/src/Advisory/Types/GenericsVariable.ql
|
||||
ql/java/ql/src/AlertSuppression.ql
|
||||
ql/java/ql/src/AlertSuppressionAnnotations.ql
|
||||
ql/java/ql/src/Architecture/Dependencies/MutualDependency.ql
|
||||
@@ -31,10 +14,6 @@ ql/java/ql/src/Architecture/Refactoring Opportunities/HubClasses.ql
|
||||
ql/java/ql/src/Architecture/Refactoring Opportunities/InappropriateIntimacy.ql
|
||||
ql/java/ql/src/Complexity/BlockWithTooManyStatements.ql
|
||||
ql/java/ql/src/Complexity/ComplexCondition.ql
|
||||
ql/java/ql/src/DeadCode/DeadClass.ql
|
||||
ql/java/ql/src/DeadCode/DeadEnumConstant.ql
|
||||
ql/java/ql/src/DeadCode/DeadField.ql
|
||||
ql/java/ql/src/DeadCode/DeadMethod.ql
|
||||
ql/java/ql/src/DeadCode/FLinesOfDeadCode.ql
|
||||
ql/java/ql/src/Frameworks/JavaEE/EJB/EjbContainerInterference.ql
|
||||
ql/java/ql/src/Frameworks/JavaEE/EJB/EjbFileIO.ql
|
||||
@@ -66,7 +45,6 @@ ql/java/ql/src/Frameworks/Spring/XML Configuration Errors/MissingSetters.ql
|
||||
ql/java/ql/src/Language Abuse/CastThisToTypeParameter.ql
|
||||
ql/java/ql/src/Language Abuse/DubiousDowncastOfThis.ql
|
||||
ql/java/ql/src/Language Abuse/DubiousTypeTestOfThis.ql
|
||||
ql/java/ql/src/Language Abuse/EmptyStatement.ql
|
||||
ql/java/ql/src/Language Abuse/EnumIdentifier.ql
|
||||
ql/java/ql/src/Language Abuse/ImplementsAnnotation.ql
|
||||
ql/java/ql/src/Language Abuse/MissedTernaryOpportunity.ql
|
||||
@@ -187,7 +165,6 @@ ql/java/ql/src/Violations of Best Practice/Magic Constants/MagicNumbersUseConsta
|
||||
ql/java/ql/src/Violations of Best Practice/Magic Constants/MagicStringsUseConstant.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverridesNames.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/LocalShadowsField.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToSystemExit.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/GarbageCollection.ql
|
||||
ql/java/ql/src/Violations of Best Practice/legacy/AutoBoxing.ql
|
||||
ql/java/ql/src/Violations of Best Practice/legacy/FinallyMayNotComplete.ql
|
||||
@@ -196,7 +173,6 @@ ql/java/ql/src/Violations of Best Practice/legacy/ParameterAssignment.ql
|
||||
ql/java/ql/src/Violations of Best Practice/legacy/UnnecessaryCast.ql
|
||||
ql/java/ql/src/Violations of Best Practice/legacy/UnnecessaryImport.ql
|
||||
ql/java/ql/src/definitions.ql
|
||||
ql/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
|
||||
ql/java/ql/src/experimental/Security/CWE/CWE-020/Log4jJndiInjection.ql
|
||||
ql/java/ql/src/experimental/Security/CWE/CWE-036/OpenStream.ql
|
||||
ql/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql
|
||||
|
||||
@@ -1,3 +1,36 @@
|
||||
## 7.7.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 7.7.0
|
||||
|
||||
### New Features
|
||||
|
||||
* The Java extractor and QL libraries now support Java 25.
|
||||
* Added support for Java 25 compact source files (JEP 512). The new predicate `Class.isImplicit()` identifies classes that are implicitly declared when using compact source files, and the new predicate `CompilationUnit.isCompactSourceFile()` identifies compilation units that contain compact source files.
|
||||
* Added support for Java 25 module import declarations.
|
||||
* Add `ModuleImportDeclaration` class.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Improved support for various assertion libraries, in particular JUnit. This affects the control-flow graph slightly, and in turn affects several queries (mainly quality queries). Most queries should see improved precision (new true positives and fewer false positives), in particular `java/constant-comparison`, `java/index-out-of-bounds`, `java/dereferenced-value-may-be-null`, and `java/useless-null-check`. Some medium precision queries like `java/toctou-race-condition` and `java/unreleased-lock` may see mixed result changes (both slight improvements and slight regressions).
|
||||
* Added taint flow model for `java.crypto.KDF`.
|
||||
* Added taint flow model for `java.lang.ScopedValue`.
|
||||
|
||||
## 7.6.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 7.6.0
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Added library models for the relevant method calls under `jakarta.servlet.ServletRequest` and `jakarta.servlet.http.HttpServletRequest` as remote flow sources.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Guard implication logic involving wrapper methods has been improved. In particular, this means fewer false positives for `java/dereferenced-value-may-be-null`.
|
||||
|
||||
## 7.5.0
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Guard implication logic involving wrapper methods has been improved. In particular, this means fewer false positives for `java/dereferenced-value-may-be-null`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* Added library models for the relevant method calls under `jakarta.servlet.ServletRequest` and `jakarta.servlet.http.HttpServletRequest` as remote flow sources.
|
||||
9
java/ql/lib/change-notes/released/7.6.0.md
Normal file
9
java/ql/lib/change-notes/released/7.6.0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## 7.6.0
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Added library models for the relevant method calls under `jakarta.servlet.ServletRequest` and `jakarta.servlet.http.HttpServletRequest` as remote flow sources.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Guard implication logic involving wrapper methods has been improved. In particular, this means fewer false positives for `java/dereferenced-value-may-be-null`.
|
||||
3
java/ql/lib/change-notes/released/7.6.1.md
Normal file
3
java/ql/lib/change-notes/released/7.6.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 7.6.1
|
||||
|
||||
No user-facing changes.
|
||||
14
java/ql/lib/change-notes/released/7.7.0.md
Normal file
14
java/ql/lib/change-notes/released/7.7.0.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## 7.7.0
|
||||
|
||||
### New Features
|
||||
|
||||
* The Java extractor and QL libraries now support Java 25.
|
||||
* Added support for Java 25 compact source files (JEP 512). The new predicate `Class.isImplicit()` identifies classes that are implicitly declared when using compact source files, and the new predicate `CompilationUnit.isCompactSourceFile()` identifies compilation units that contain compact source files.
|
||||
* Added support for Java 25 module import declarations.
|
||||
* Add `ModuleImportDeclaration` class.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Improved support for various assertion libraries, in particular JUnit. This affects the control-flow graph slightly, and in turn affects several queries (mainly quality queries). Most queries should see improved precision (new true positives and fewer false positives), in particular `java/constant-comparison`, `java/index-out-of-bounds`, `java/dereferenced-value-may-be-null`, and `java/useless-null-check`. Some medium precision queries like `java/toctou-race-condition` and `java/unreleased-lock` may see mixed result changes (both slight improvements and slight regressions).
|
||||
* Added taint flow model for `java.crypto.KDF`.
|
||||
* Added taint flow model for `java.lang.ScopedValue`.
|
||||
3
java/ql/lib/change-notes/released/7.7.1.md
Normal file
3
java/ql/lib/change-notes/released/7.7.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 7.7.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 7.5.0
|
||||
lastReleaseVersion: 7.7.1
|
||||
|
||||
@@ -537,6 +537,10 @@ isLocalClassOrInterface(
|
||||
int parent: @localtypedeclstmt ref
|
||||
);
|
||||
|
||||
isImplicitClass(
|
||||
unique int classid: @classorinterface ref
|
||||
);
|
||||
|
||||
isDefConstr(
|
||||
int constructorid: @constructor ref
|
||||
);
|
||||
|
||||
@@ -13986,6 +13986,17 @@
|
||||
</dep>
|
||||
</dependencies>
|
||||
</relation>
|
||||
<relation>
|
||||
<name>isImplicitClass</name>
|
||||
<cardinality>4</cardinality>
|
||||
<columnsizes>
|
||||
<e>
|
||||
<k>classid</k>
|
||||
<v>4</v>
|
||||
</e>
|
||||
</columnsizes>
|
||||
<dependencies/>
|
||||
</relation>
|
||||
<relation>
|
||||
<name>isDefConstr</name>
|
||||
<cardinality>139376</cardinality>
|
||||
|
||||
@@ -263,7 +263,7 @@ module JCAModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Data-flow configuration modelling flow from a cipher string literal to a cipher algorithm consumer.
|
||||
* Data-flow configuration modeling flow from a cipher string literal to a cipher algorithm consumer.
|
||||
*/
|
||||
private module CipherAlgorithmStringToCipherConsumerConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof CipherStringLiteral }
|
||||
@@ -398,9 +398,7 @@ module JCAModel {
|
||||
* For example, in `Cipher.getInstance(algorithm)`, this class represents `algorithm`.
|
||||
*/
|
||||
class CipherGetInstanceAlgorithmArg extends CipherAlgorithmValueConsumer instanceof Expr {
|
||||
CipherGetInstanceCall call;
|
||||
|
||||
CipherGetInstanceAlgorithmArg() { this = call.getAlgorithmArg() }
|
||||
CipherGetInstanceAlgorithmArg() { this = any(CipherGetInstanceCall call).getAlgorithmArg() }
|
||||
|
||||
override Crypto::ConsumerInputDataFlowNode getInputNode() { result.asExpr() = this }
|
||||
|
||||
@@ -1363,7 +1361,7 @@ module JCAModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* Data-flow configuration modelling flow from a key agreement string literal to a key agreement algorithm consumer.
|
||||
* Data-flow configuration modeling flow from a key agreement string literal to a key agreement algorithm consumer.
|
||||
*/
|
||||
private module KeyAgreementAlgorithmStringToConsumerConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof KeyAgreementStringLiteral }
|
||||
@@ -1586,11 +1584,9 @@ module JCAModel {
|
||||
}
|
||||
|
||||
class MacOperationCall extends Crypto::MacOperationInstance instanceof MethodCall {
|
||||
Expr output;
|
||||
|
||||
MacOperationCall() {
|
||||
super.getMethod().getDeclaringType().hasQualifiedName("javax.crypto", "Mac") and
|
||||
(
|
||||
exists(Expr output |
|
||||
super.getMethod().hasStringSignature(["doFinal()", "doFinal(byte[])"]) and this = output
|
||||
or
|
||||
super.getMethod().hasStringSignature("doFinal(byte[], int)") and
|
||||
|
||||
@@ -113,7 +113,7 @@ private class ConstantDataSource extends Crypto::GenericConstantSourceInstance i
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of random number generation, modelled as the expression
|
||||
* An instance of random number generation, modeled as the expression
|
||||
* tied to an output node (i.e., the result of the source of randomness)
|
||||
*/
|
||||
abstract class RandomnessInstance extends Crypto::RandomNumberGenerationInstance {
|
||||
|
||||
19
java/ql/lib/ext/java.lang.scoped.model.yml
Normal file
19
java/ql/lib/ext/java.lang.scoped.model.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["java.lang", "ScopedValue", False, "where", "(ScopedValue,Object)", "", "Argument[1]", "Argument[0].SyntheticField[java.lang.ScopedValue.boundValue]", "value", "manual"]
|
||||
- ["java.lang", "ScopedValue", True, "get", "()", "", "Argument[this].SyntheticField[java.lang.ScopedValue.boundValue]", "ReturnValue", "value", "manual"]
|
||||
- ["java.lang", "ScopedValue", False, "where", "(ScopedValue,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "ScopedValue$Carrier", False, "where", "(ScopedValue,Object)", "", "Argument[1]", "Argument[0].SyntheticField[java.lang.ScopedValue.boundValue]", "value", "manual"]
|
||||
- ["java.lang", "ScopedValue$Carrier", False, "where", "(ScopedValue,Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "ScopedValue$Carrier", False, "run", "(Runnable)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "ScopedValue$Carrier", False, "call", "(Callable)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.lang", "ScopedValue", "newInstance", "()", "summary", "manual"]
|
||||
- ["java.lang", "ScopedValue", "isBound", "()", "summary", "manual"]
|
||||
@@ -7,6 +7,21 @@ extensions:
|
||||
- ["javax.crypto", "Cipher", True, "init", "(int,Key,AlgorithmParameterSpec,SecureRandom)", "", "Argument[2]", "encryption-iv", "manual"]
|
||||
- ["javax.crypto", "Cipher", False, "unwrap", "(byte[],String,int)", "", "Argument[0]", "credentials-key", "hq-generated"]
|
||||
- ["javax.crypto", "CipherSpi", True, "engineUnwrap", "(byte[],String,int)", "", "Argument[0]", "credentials-key", "hq-generated"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["javax.crypto", "KDF", False, "getInstance", "(String)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
|
||||
- ["javax.crypto", "KDF", False, "getInstance", "(String,Provider)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
|
||||
- ["javax.crypto", "KDF", False, "getInstance", "(String,String)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
|
||||
- ["javax.crypto", "KDF", False, "getInstance", "(String,KDFParameters)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
|
||||
- ["javax.crypto", "KDF", False, "getInstance", "(String,KDFParameters,Provider)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
|
||||
- ["javax.crypto", "KDF", False, "getInstance", "(String,KDFParameters,String)", "", "Argument[0]", "ReturnValue.SyntheticField[javax.crypto.KDF.algorithm]", "value", "manual"]
|
||||
- ["javax.crypto", "KDF", True, "getAlgorithm", "()", "", "Argument[this].SyntheticField[javax.crypto.KDF.algorithm]", "ReturnValue", "value", "manual"]
|
||||
- ["javax.crypto", "KDF", True, "getProvider", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
|
||||
- ["javax.crypto", "KDF", True, "deriveKey", "(String,AlgorithmParameterSpec)", "", "Argument[1]", "ReturnValue", "taint", "manual"]
|
||||
- ["javax.crypto", "KDF", True, "deriveData", "(AlgorithmParameterSpec)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["javax.crypto", "SecretKey", True, "getEncoded", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
|
||||
@@ -7,6 +7,21 @@ extensions:
|
||||
- ["javax.crypto.spec", "GCMParameterSpec", True, "GCMParameterSpec", "", "", "Argument[1]", "Argument[this]", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "RC2ParameterSpec", True, "RC2ParameterSpec", "", "", "Argument[1]", "Argument[this]", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "RC5ParameterSpec", True, "RC5ParameterSpec", "", "", "Argument[3]", "Argument[this]", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addIKM", "(byte[])", "", "Argument[0]", "Argument[this]", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addIKM", "(byte[])", "", "Argument[this]", "ReturnValue", "value", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addIKM", "(SecretKey)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addIKM", "(SecretKey)", "", "Argument[this]", "ReturnValue", "value", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addSalt", "(byte[])", "", "Argument[0]", "Argument[this]", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addSalt", "(byte[])", "", "Argument[this]", "ReturnValue", "value", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addSalt", "(SecretKey)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "addSalt", "(SecretKey)", "", "Argument[this]", "ReturnValue", "value", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "thenExpand", "(byte[],int)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "thenExpand", "(byte[],int)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec$Builder", True, "thenExpand", "(byte[],int)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec", False, "expandOnly", "(SecretKey,byte[],int)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "HKDFParameterSpec", False, "expandOnly", "(SecretKey,byte[],int)", "", "Argument[1]", "ReturnValue", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "SecretKeySpec", False, "SecretKeySpec", "(byte[],String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
|
||||
- ["javax.crypto.spec", "SecretKeySpec", False, "SecretKeySpec", "(byte[],int,int,String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: sinkModel
|
||||
|
||||
@@ -18,8 +18,8 @@ external string selectedSourceFile();
|
||||
|
||||
class PrintAstConfigurationOverride extends PrintAstConfiguration {
|
||||
/**
|
||||
* Holds if the location matches the selected file in the VS Code extension and
|
||||
* the element is `fromSource`.
|
||||
* Holds if the location `l` matches the selected file in the VS Code extension and
|
||||
* the element `e` is `fromSource`.
|
||||
*/
|
||||
override predicate shouldPrint(Element e, Location l) {
|
||||
super.shouldPrint(e, l) and
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-all
|
||||
version: 7.5.1-dev
|
||||
version: 7.7.2-dev
|
||||
groups: java
|
||||
dbscheme: config/semmlecode.dbscheme
|
||||
extractor: java
|
||||
|
||||
@@ -70,7 +70,12 @@ class ConfigValue extends @configValue, ConfigLocatable {
|
||||
override string toString() { result = this.getValue() }
|
||||
}
|
||||
|
||||
/** A `.properties` file. */
|
||||
class PropertiesFile extends File {
|
||||
PropertiesFile() { this.getExtension() = "properties" }
|
||||
}
|
||||
|
||||
/** A Java property is a name-value pair in a `.properties` file. */
|
||||
class JavaProperty extends ConfigPair {
|
||||
JavaProperty() { this.getFile().getExtension() = "properties" }
|
||||
JavaProperty() { this.getFile() instanceof PropertiesFile }
|
||||
}
|
||||
|
||||
@@ -33,5 +33,13 @@ class CompilationUnit extends Element, File {
|
||||
*/
|
||||
Module getModule() { cumodule(this, result) }
|
||||
|
||||
/**
|
||||
* Holds if this compilation unit represents a compact source file.
|
||||
* A compact source file contains an implicitly declared top-level class.
|
||||
*/
|
||||
predicate isCompactSourceFile() {
|
||||
exists(Class c | c.getCompilationUnit() = this and c.isImplicit())
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CompilationUnit" }
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ predicate locallySynchronizedOnThis(Expr e, RefType thisType) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is synchronized by a `synchronized` modifier on the enclosing (static) method.
|
||||
* Holds if `e` is synchronized by a `synchronized` modifier on the enclosing (static) method
|
||||
* declared in the type `classType`.
|
||||
*/
|
||||
predicate locallySynchronizedOnClass(Expr e, RefType classType) {
|
||||
exists(SynchronizedCallable c | c = e.getEnclosingCallable() |
|
||||
|
||||
@@ -82,6 +82,7 @@ module;
|
||||
*/
|
||||
|
||||
import java
|
||||
private import codeql.controlflow.SuccessorType
|
||||
private import codeql.util.Boolean
|
||||
private import Completion
|
||||
private import controlflow.internal.Preconditions
|
||||
@@ -124,6 +125,28 @@ module ControlFlow {
|
||||
result = succ(this, NormalCompletion())
|
||||
}
|
||||
|
||||
/** Gets an immediate successor of this node of a given type, if any. */
|
||||
Node getASuccessor(SuccessorType t) {
|
||||
result = branchSuccessor(this, t.(BooleanSuccessor).getValue())
|
||||
or
|
||||
exists(Completion completion |
|
||||
result = succ(this, completion) and
|
||||
not result = branchSuccessor(this, _)
|
||||
|
|
||||
completion = NormalCompletion() and t instanceof DirectSuccessor
|
||||
or
|
||||
completion = ReturnCompletion() and t instanceof ReturnSuccessor
|
||||
or
|
||||
completion = BreakCompletion(_) and t instanceof BreakSuccessor
|
||||
or
|
||||
completion = YieldCompletion(_) and t instanceof BreakSuccessor
|
||||
or
|
||||
completion = ContinueCompletion(_) and t instanceof ContinueSuccessor
|
||||
or
|
||||
completion = ThrowCompletion(_) and t instanceof ExceptionSuccessor
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the basic block that contains this node. */
|
||||
BasicBlock getBasicBlock() { result.getANode() = this }
|
||||
|
||||
@@ -365,10 +388,10 @@ private module ControlFlowGraphImpl {
|
||||
* Bind `t` to an unchecked exception that may occur in a precondition check or guard wrapper.
|
||||
*/
|
||||
private predicate uncheckedExceptionFromMethod(MethodCall ma, ThrowableType t) {
|
||||
conditionCheckArgument(ma, _, _) and
|
||||
(methodCallChecksArgument(ma) or methodCallUnconditionallyThrows(ma)) and
|
||||
(t instanceof TypeError or t instanceof TypeRuntimeException)
|
||||
or
|
||||
methodMayThrow(ma.getMethod(), t)
|
||||
methodMayThrow(ma.getMethod().getSourceDeclaration(), t)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -586,6 +609,7 @@ private module ControlFlowGraphImpl {
|
||||
* Gets a `MethodCall` that always throws an exception or calls `exit`.
|
||||
*/
|
||||
private MethodCall nonReturningMethodCall() {
|
||||
methodCallUnconditionallyThrows(result) or
|
||||
result.getMethod().getSourceDeclaration() = nonReturningMethod() or
|
||||
result = likelyNonReturningMethod().getAnAccess()
|
||||
}
|
||||
|
||||
@@ -100,12 +100,12 @@ class InvocationConversionContext extends ConversionSite {
|
||||
* See the Java Language Specification, Section 5.4.
|
||||
*/
|
||||
class StringConversionContext extends ConversionSite {
|
||||
AddExpr a;
|
||||
|
||||
StringConversionContext() {
|
||||
a.getAnOperand() = this and
|
||||
not this.getType() instanceof TypeString and
|
||||
a.getAnOperand().getType() instanceof TypeString
|
||||
exists(AddExpr a |
|
||||
a.getAnOperand() = this and
|
||||
not this.getType() instanceof TypeString and
|
||||
a.getAnOperand().getType() instanceof TypeString
|
||||
)
|
||||
}
|
||||
|
||||
override Type getConversionTarget() { result instanceof TypeString }
|
||||
|
||||
@@ -154,3 +154,35 @@ class ImportStaticTypeMember extends Import {
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ImportStaticTypeMember" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A module import declaration, which imports an entire module.
|
||||
*
|
||||
* For example, `import module java.base;` makes all packages exported
|
||||
* by the `java.base` module available, and through those packages,
|
||||
* the types they declare become accessible.
|
||||
*/
|
||||
class ModuleImportDeclaration extends Import {
|
||||
ModuleImportDeclaration() { imports(this, _, _, 6) }
|
||||
|
||||
/** Gets the name of the imported module. */
|
||||
string getModuleName() { imports(this, _, result, _) }
|
||||
|
||||
/** Gets the imported module. */
|
||||
Module getModule() { result.getName() = this.getModuleName() }
|
||||
|
||||
/** Gets an exported package from the imported module. */
|
||||
Package getAnImportedPackage() {
|
||||
exists(ExportsDirective exports |
|
||||
exports = this.getModule().getADirective() and
|
||||
result = exports.getExportedPackage()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a type from a package that is accessible through this module import. */
|
||||
RefType getAnImportedType() { result.getPackage() = this.getAnImportedPackage() }
|
||||
|
||||
override string toString() { result = "import module " + this.getModuleName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ModuleImportDeclaration" }
|
||||
}
|
||||
|
||||
@@ -848,6 +848,9 @@ class Field extends Member, ExprParent, @field, Variable {
|
||||
override string getAPrimaryQlClass() { result = "Field" }
|
||||
}
|
||||
|
||||
overlay[local]
|
||||
private class DiscardableField extends DiscardableReferableLocatable, @field { }
|
||||
|
||||
/** An instance field. */
|
||||
class InstanceField extends Field {
|
||||
InstanceField() { not this.isStatic() }
|
||||
|
||||
@@ -18,7 +18,7 @@ predicate isOverlay() { databaseMetadata("isOverlay", "true") }
|
||||
overlay[local]
|
||||
string getRawFile(@locatable el) {
|
||||
exists(@location loc, @file file |
|
||||
hasLocation(el, loc) and
|
||||
(hasLocation(el, loc) or xmllocations(el, loc)) and
|
||||
locations_default(loc, file, _, _, _, _) and
|
||||
files(file, result)
|
||||
)
|
||||
@@ -73,40 +73,60 @@ private predicate discardReferableLocatable(@locatable el) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the raw file for a configLocatable. */
|
||||
overlay[local]
|
||||
private predicate baseConfigLocatable(@configLocatable l) { not isOverlay() and exists(l) }
|
||||
private string getRawFileForConfig(@configLocatable el) {
|
||||
exists(@location loc, @file file |
|
||||
configLocations(el, loc) and
|
||||
locations_default(loc, file, _, _, _, _) and
|
||||
files(file, result)
|
||||
)
|
||||
}
|
||||
|
||||
overlay[local]
|
||||
private predicate overlayHasConfigLocatables() {
|
||||
private string baseConfigLocatable(@configLocatable el) {
|
||||
not isOverlay() and result = getRawFileForConfig(el)
|
||||
}
|
||||
|
||||
overlay[local]
|
||||
private predicate overlayConfigExtracted(string file) {
|
||||
isOverlay() and
|
||||
exists(@configLocatable el)
|
||||
exists(@configLocatable el | file = getRawFileForConfig(el))
|
||||
}
|
||||
|
||||
overlay[discard_entity]
|
||||
private predicate discardBaseConfigLocatable(@configLocatable el) {
|
||||
// The properties extractor is currently not incremental, so if
|
||||
// the overlay contains any config locatables, the overlay should
|
||||
// contain a full extraction and all config locatables from base
|
||||
// should be discarded.
|
||||
baseConfigLocatable(el) and overlayHasConfigLocatables()
|
||||
overlayChangedFiles(baseConfigLocatable(el))
|
||||
or
|
||||
// The config extractor is currently not incremental and may extract more
|
||||
// property files than those included in overlayChangedFiles.
|
||||
overlayConfigExtracted(baseConfigLocatable(el))
|
||||
}
|
||||
|
||||
/**
|
||||
* An `@xmllocatable` that should be discarded in the base variant if its file is
|
||||
* extracted in the overlay variant.
|
||||
*/
|
||||
overlay[local]
|
||||
abstract class DiscardableXmlLocatable extends @xmllocatable {
|
||||
/** Gets the raw file for an xmllocatable in base. */
|
||||
string getRawFileInBase() { not isOverlay() and result = getRawFile(this) }
|
||||
|
||||
/** Gets a textual representation of this discardable xmllocatable. */
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
overlay[local]
|
||||
private predicate baseXmlLocatable(@xmllocatable l) {
|
||||
not isOverlay() and not files(l, _) and not xmlNs(l, _, _, _)
|
||||
}
|
||||
|
||||
overlay[local]
|
||||
private predicate overlayHasXmlLocatable() {
|
||||
private predicate overlayXmlExtracted(string file) {
|
||||
isOverlay() and
|
||||
exists(@xmllocatable l | not files(l, _) and not xmlNs(l, _, _, _))
|
||||
exists(@xmllocatable el | not files(el, _) and not xmlNs(el, _, _, _) and file = getRawFile(el))
|
||||
}
|
||||
|
||||
overlay[discard_entity]
|
||||
private predicate discardBaseXmlLocatable(@xmllocatable el) {
|
||||
// The XML extractor is currently not incremental, so if
|
||||
// the overlay contains any XML locatables, the overlay should
|
||||
// contain a full extraction and all XML locatables from base
|
||||
// should be discarded.
|
||||
baseXmlLocatable(el) and overlayHasXmlLocatable()
|
||||
private predicate discardXmlLocatable(@xmllocatable el) {
|
||||
overlayChangedFiles(el.(DiscardableXmlLocatable).getRawFileInBase())
|
||||
or
|
||||
// The XML extractor is currently not incremental and may extract more
|
||||
// XML files than those included in overlayChangedFiles.
|
||||
overlayXmlExtracted(el.(DiscardableXmlLocatable).getRawFileInBase())
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ class TryStmt extends Stmt, @trystmt {
|
||||
CatchClause getACatchClause() { result.getParent() = this }
|
||||
|
||||
/**
|
||||
* Gets the `catch` clause at the specified (zero-based) position
|
||||
* Gets the `catch` clause at the specified (zero-based) position `index`
|
||||
* in this `try` statement.
|
||||
*/
|
||||
CatchClause getCatchClause(int index) {
|
||||
@@ -305,7 +305,7 @@ class TryStmt extends Stmt, @trystmt {
|
||||
/** Gets a resource variable declaration, if any. */
|
||||
LocalVariableDeclStmt getAResourceDecl() { result.getParent() = this and result.getIndex() <= -3 }
|
||||
|
||||
/** Gets the resource variable declaration at the specified position in this `try` statement. */
|
||||
/** Gets the resource variable declaration at the specified position `index` in this `try` statement. */
|
||||
LocalVariableDeclStmt getResourceDecl(int index) {
|
||||
result = this.getAResourceDecl() and
|
||||
index = -3 - result.getIndex()
|
||||
@@ -314,7 +314,7 @@ class TryStmt extends Stmt, @trystmt {
|
||||
/** Gets a resource expression, if any. */
|
||||
VarAccess getAResourceExpr() { result.getParent() = this and result.getIndex() <= -3 }
|
||||
|
||||
/** Gets the resource expression at the specified position in this `try` statement. */
|
||||
/** Gets the resource expression at the specified position `index` in this `try` statement. */
|
||||
VarAccess getResourceExpr(int index) {
|
||||
result = this.getAResourceExpr() and
|
||||
index = -3 - result.getIndex()
|
||||
@@ -323,7 +323,7 @@ class TryStmt extends Stmt, @trystmt {
|
||||
/** Gets a resource in this `try` statement, if any. */
|
||||
ExprParent getAResource() { result = this.getAResourceDecl() or result = this.getAResourceExpr() }
|
||||
|
||||
/** Gets the resource at the specified position in this `try` statement. */
|
||||
/** Gets the resource at the specified position `index` in this `try` statement. */
|
||||
ExprParent getResource(int index) {
|
||||
result = this.getResourceDecl(index) or result = this.getResourceExpr(index)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ module;
|
||||
import Member
|
||||
import Modifier
|
||||
import JDK
|
||||
private import semmle.code.java.Overlay
|
||||
|
||||
/**
|
||||
* Holds if reference type `t` is an immediate super-type of `sub`.
|
||||
@@ -699,6 +700,15 @@ class Class extends ClassOrInterface {
|
||||
/** Holds if this class is an anonymous class. */
|
||||
predicate isAnonymous() { isAnonymClass(this.getSourceDeclaration(), _) }
|
||||
|
||||
/** Holds if this class is an implicit class (compact source file). */
|
||||
predicate isImplicit() { isImplicitClass(this.getSourceDeclaration()) }
|
||||
|
||||
/** Holds if this is an auxiliary program element generated by the compiler. */
|
||||
override predicate isCompilerGenerated() {
|
||||
super.isCompilerGenerated() or
|
||||
this.isImplicit()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an annotation that applies to this class.
|
||||
*
|
||||
@@ -998,6 +1008,10 @@ class ClassOrInterface extends RefType, @classorinterface {
|
||||
CompanionObject getCompanionObject() { type_companion_object(this, _, result) }
|
||||
}
|
||||
|
||||
overlay[local]
|
||||
private class DiscardableClassOrInterface extends DiscardableReferableLocatable, @classorinterface {
|
||||
}
|
||||
|
||||
private string getAPublicObjectMethodSignature() {
|
||||
exists(Method m |
|
||||
m.getDeclaringType() instanceof TypeObject and
|
||||
|
||||
@@ -7,10 +7,9 @@ module;
|
||||
import java
|
||||
import Dominance
|
||||
private import codeql.controlflow.BasicBlock as BB
|
||||
private import codeql.controlflow.SuccessorType
|
||||
|
||||
private module Input implements BB::InputSig<Location> {
|
||||
import SuccessorType
|
||||
|
||||
/** Hold if `t` represents a conditional successor type. */
|
||||
predicate successorTypeIsCondition(SuccessorType t) { none() }
|
||||
|
||||
@@ -23,20 +22,8 @@ private module Input implements BB::InputSig<Location> {
|
||||
/** Gets the CFG scope in which this node occurs. */
|
||||
CfgScope nodeGetCfgScope(Node node) { node.getEnclosingCallable() = result }
|
||||
|
||||
private Node getASpecificSuccessor(Node node, SuccessorType t) {
|
||||
node.(ConditionNode).getABranchSuccessor(t.(BooleanSuccessor).getValue()) = result
|
||||
or
|
||||
node.getAnExceptionSuccessor() = result and t instanceof ExceptionSuccessor
|
||||
}
|
||||
|
||||
/** Gets an immediate successor of this node. */
|
||||
Node nodeGetASuccessor(Node node, SuccessorType t) {
|
||||
result = getASpecificSuccessor(node, t)
|
||||
or
|
||||
node.getASuccessor() = result and
|
||||
t instanceof NormalSuccessor and
|
||||
not result = getASpecificSuccessor(node, _)
|
||||
}
|
||||
Node nodeGetASuccessor(Node node, SuccessorType t) { result = node.getASuccessor(t) }
|
||||
|
||||
/**
|
||||
* Holds if `node` represents an entry node to be used when calculating
|
||||
@@ -96,7 +83,17 @@ class BasicBlock extends BbImpl::BasicBlock {
|
||||
predicate strictlyDominates(BasicBlock bb) { super.strictlyDominates(bb) }
|
||||
|
||||
/** Gets an immediate successor of this basic block of a given type, if any. */
|
||||
BasicBlock getASuccessor(Input::SuccessorType t) { result = super.getASuccessor(t) }
|
||||
BasicBlock getASuccessor(SuccessorType t) { result = super.getASuccessor(t) }
|
||||
|
||||
BasicBlock getASuccessor() { result = super.getASuccessor() }
|
||||
|
||||
BasicBlock getImmediateDominator() { result = super.getImmediateDominator() }
|
||||
|
||||
predicate inDominanceFrontier(BasicBlock df) { super.inDominanceFrontier(df) }
|
||||
|
||||
predicate strictlyPostDominates(BasicBlock bb) { super.strictlyPostDominates(bb) }
|
||||
|
||||
predicate postDominates(BasicBlock bb) { super.postDominates(bb) }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `getASuccessor` instead.
|
||||
@@ -145,3 +142,15 @@ class BasicBlock extends BbImpl::BasicBlock {
|
||||
class ExitBlock extends BasicBlock {
|
||||
ExitBlock() { this.getLastNode() instanceof ControlFlow::ExitNode }
|
||||
}
|
||||
|
||||
private class BasicBlockAlias = BasicBlock;
|
||||
|
||||
module Cfg implements BB::CfgSig<Location> {
|
||||
class ControlFlowNode = BbImpl::ControlFlowNode;
|
||||
|
||||
class BasicBlock = BasicBlockAlias;
|
||||
|
||||
class EntryBasicBlock extends BasicBlock instanceof BbImpl::EntryBasicBlock { }
|
||||
|
||||
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { BbImpl::dominatingEdge(bb1, bb2) }
|
||||
}
|
||||
|
||||
60
java/ql/lib/semmle/code/java/controlflow/ControlFlow.qll
Normal file
60
java/ql/lib/semmle/code/java/controlflow/ControlFlow.qll
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Provides an implementation of local (intraprocedural) control flow reachability.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
private import codeql.controlflow.ControlFlow
|
||||
private import semmle.code.java.dataflow.SSA as SSA
|
||||
private import semmle.code.java.controlflow.Guards as Guards
|
||||
|
||||
private module ControlFlowInput implements InputSig<Location, ControlFlowNode, BasicBlock> {
|
||||
private import java as J
|
||||
|
||||
AstNode getEnclosingAstNode(ControlFlowNode node) { node.getAstNode() = result }
|
||||
|
||||
class AstNode = ExprParent;
|
||||
|
||||
AstNode getParent(AstNode node) {
|
||||
result = node.(Expr).getParent() or
|
||||
result = node.(Stmt).getParent()
|
||||
}
|
||||
|
||||
class FinallyBlock extends AstNode {
|
||||
FinallyBlock() { any(TryStmt try).getFinally() = this }
|
||||
}
|
||||
|
||||
class Expr = J::Expr;
|
||||
|
||||
class SourceVariable = SSA::SsaSourceVariable;
|
||||
|
||||
class SsaDefinition = SSA::SsaVariable;
|
||||
|
||||
class SsaWriteDefinition extends SsaDefinition instanceof SSA::SsaExplicitUpdate {
|
||||
Expr getDefinition() {
|
||||
super.getDefiningExpr().(VariableAssign).getSource() = result or
|
||||
super.getDefiningExpr().(AssignOp) = result
|
||||
}
|
||||
}
|
||||
|
||||
class SsaPhiNode = SSA::SsaPhiNode;
|
||||
|
||||
class SsaUncertainDefinition extends SsaDefinition instanceof SSA::SsaUncertainImplicitUpdate {
|
||||
SsaDefinition getPriorDefinition() { result = super.getPriorDef() }
|
||||
}
|
||||
|
||||
class GuardValue = Guards::GuardValue;
|
||||
|
||||
predicate ssaControlsBranchEdge(SsaDefinition def, BasicBlock bb1, BasicBlock bb2, GuardValue v) {
|
||||
Guards::Guards_v3::ssaControlsBranchEdge(def, bb1, bb2, v)
|
||||
}
|
||||
|
||||
predicate ssaControls(SsaDefinition def, BasicBlock bb, GuardValue v) {
|
||||
Guards::Guards_v3::ssaControls(def, bb, v)
|
||||
}
|
||||
|
||||
import Guards::Guards_v3::InternalUtil
|
||||
}
|
||||
|
||||
module ControlFlow = Make<Location, Cfg, ControlFlowInput>;
|
||||
@@ -139,19 +139,13 @@ private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pre
|
||||
)
|
||||
}
|
||||
|
||||
private module GuardsInput implements SharedGuards::InputSig<Location> {
|
||||
private module GuardsInput implements SharedGuards::InputSig<Location, ControlFlowNode, BasicBlock> {
|
||||
private import java as J
|
||||
private import semmle.code.java.dataflow.internal.BaseSSA
|
||||
private import semmle.code.java.dataflow.NullGuards as NullGuards
|
||||
import SuccessorType
|
||||
|
||||
class ControlFlowNode = J::ControlFlowNode;
|
||||
|
||||
class NormalExitNode = ControlFlow::NormalExitNode;
|
||||
|
||||
class BasicBlock = J::BasicBlock;
|
||||
|
||||
predicate dominatingEdge(BasicBlock bb1, BasicBlock bb2) { J::dominatingEdge(bb1, bb2) }
|
||||
|
||||
class AstNode = ExprParent;
|
||||
|
||||
class Expr = J::Expr;
|
||||
@@ -216,6 +210,12 @@ private module GuardsInput implements SharedGuards::InputSig<Location> {
|
||||
f.isFinal() and
|
||||
f.getInitializer() = NullGuards::baseNotNullExpr()
|
||||
)
|
||||
or
|
||||
exists(CatchClause cc, LocalVariableDeclExpr decl, BaseSsaUpdate v |
|
||||
decl = cc.getVariable() and
|
||||
decl = v.getDefiningExpr() and
|
||||
this = v.getAUse()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@ private module GuardsInput implements SharedGuards::InputSig<Location> {
|
||||
}
|
||||
}
|
||||
|
||||
private module GuardsImpl = SharedGuards::Make<Location, GuardsInput>;
|
||||
private module GuardsImpl = SharedGuards::Make<Location, Cfg, GuardsInput>;
|
||||
|
||||
private module LogicInputCommon {
|
||||
private import semmle.code.java.dataflow.NullGuards as NullGuards
|
||||
@@ -395,11 +395,13 @@ private module LogicInputCommon {
|
||||
predicate additionalImpliesStep(
|
||||
GuardsImpl::PreGuard g1, GuardValue v1, GuardsImpl::PreGuard g2, GuardValue v2
|
||||
) {
|
||||
exists(MethodCall check, int argIndex |
|
||||
exists(MethodCall check |
|
||||
g1 = check and
|
||||
v1.getDualValue().isThrowsException() and
|
||||
conditionCheckArgument(check, argIndex, v2.asBooleanValue()) and
|
||||
g2 = check.getArgument(argIndex)
|
||||
v1.getDualValue().isThrowsException()
|
||||
|
|
||||
methodCallChecksBoolean(check, g2, v2.asBooleanValue())
|
||||
or
|
||||
methodCallChecksNotNull(check, g2) and v2.isNonNullValue()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* Provides different types of control flow successor types.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
private import codeql.util.Boolean
|
||||
|
||||
private newtype TSuccessorType =
|
||||
TNormalSuccessor() or
|
||||
TBooleanSuccessor(Boolean branch) or
|
||||
TExceptionSuccessor()
|
||||
|
||||
/** The type of a control flow successor. */
|
||||
class SuccessorType extends TSuccessorType {
|
||||
/** Gets a textual representation of successor type. */
|
||||
string toString() { result = "SuccessorType" }
|
||||
}
|
||||
|
||||
/** A normal control flow successor. */
|
||||
class NormalSuccessor extends SuccessorType, TNormalSuccessor { }
|
||||
|
||||
/**
|
||||
* An exceptional control flow successor.
|
||||
*
|
||||
* This marks control flow edges that are taken when an exception is thrown.
|
||||
*/
|
||||
class ExceptionSuccessor extends SuccessorType, TExceptionSuccessor { }
|
||||
|
||||
/**
|
||||
* A conditional control flow successor.
|
||||
*
|
||||
* This currently only includes boolean successors (`BooleanSuccessor`).
|
||||
*/
|
||||
class ConditionalSuccessor extends SuccessorType, TBooleanSuccessor {
|
||||
/** Gets the Boolean value of this successor. */
|
||||
boolean getValue() { this = TBooleanSuccessor(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Boolean control flow successor.
|
||||
*
|
||||
* For example, this program fragment:
|
||||
*
|
||||
* ```java
|
||||
* if (x < 0)
|
||||
* return 0;
|
||||
* else
|
||||
* return 1;
|
||||
* ```
|
||||
*
|
||||
* has a control flow graph containing Boolean successors:
|
||||
*
|
||||
* ```
|
||||
* if
|
||||
* |
|
||||
* x < 0
|
||||
* / \
|
||||
* / \
|
||||
* / \
|
||||
* true false
|
||||
* | \
|
||||
* return 0 return 1
|
||||
* ```
|
||||
*/
|
||||
class BooleanSuccessor = ConditionalSuccessor;
|
||||
|
||||
/**
|
||||
* A nullness control flow successor. This is currently unused for Java.
|
||||
*/
|
||||
class NullnessSuccessor extends ConditionalSuccessor {
|
||||
NullnessSuccessor() { none() }
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Provides predicates for identifying precondition checks like
|
||||
* Provides predicates for identifying precondition and assertion checks like
|
||||
* `com.google.common.base.Preconditions` and
|
||||
* `org.apache.commons.lang3.Validate`.
|
||||
*/
|
||||
@@ -9,99 +9,150 @@ module;
|
||||
import java
|
||||
|
||||
/**
|
||||
* Holds if `m` is a method that checks that its argument at position `arg` is
|
||||
* equal to true and throws otherwise.
|
||||
*/
|
||||
private predicate methodCheckTrue(Method m, int arg) {
|
||||
arg = 0 and
|
||||
(
|
||||
m.hasQualifiedName("com.google.common.base", "Preconditions", ["checkArgument", "checkState"]) or
|
||||
m.hasQualifiedName("com.google.common.base", "Verify", "verify") or
|
||||
m.hasQualifiedName("org.apache.commons.lang3", "Validate", ["isTrue", "validState"]) or
|
||||
m.hasQualifiedName("org.junit.jupiter.api", "Assertions", "assertTrue") or
|
||||
m.hasQualifiedName("org.junit.jupiter.api", "Assumptions", "assumeTrue") or
|
||||
m.hasQualifiedName("org.testng", "Assert", "assertTrue")
|
||||
)
|
||||
or
|
||||
m.getParameter(arg).getType() instanceof BooleanType and
|
||||
(
|
||||
m.hasQualifiedName("org.junit", "Assert", "assertTrue") or
|
||||
m.hasQualifiedName("org.junit", "Assume", "assumeTrue") or
|
||||
m.hasQualifiedName("junit.framework", _, "assertTrue")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `m` is a method that checks that its argument at position `arg` is
|
||||
* equal to false and throws otherwise.
|
||||
*/
|
||||
private predicate methodCheckFalse(Method m, int arg) {
|
||||
arg = 0 and
|
||||
(
|
||||
m.hasQualifiedName("org.junit.jupiter.api", "Assertions", "assertFalse") or
|
||||
m.hasQualifiedName("org.junit.jupiter.api", "Assumptions", "assumeFalse") or
|
||||
m.hasQualifiedName("org.testng", "Assert", "assertFalse")
|
||||
)
|
||||
or
|
||||
m.getParameter(arg).getType() instanceof BooleanType and
|
||||
(
|
||||
m.hasQualifiedName("org.junit", "Assert", "assertFalse") or
|
||||
m.hasQualifiedName("org.junit", "Assume", "assumeFalse") or
|
||||
m.hasQualifiedName("junit.framework", _, "assertFalse")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `m` is a method that checks that its argument at position `arg` is
|
||||
* not null and throws otherwise.
|
||||
*/
|
||||
private predicate methodCheckNotNull(Method m, int arg) {
|
||||
arg = 0 and
|
||||
(
|
||||
m.hasQualifiedName("com.google.common.base", "Preconditions", "checkNotNull") or
|
||||
m.hasQualifiedName("com.google.common.base", "Verify", "verifyNotNull") or
|
||||
m.hasQualifiedName("org.apache.commons.lang3", "Validate", "notNull") or
|
||||
m.hasQualifiedName("java.util", "Objects", "requireNonNull") or
|
||||
m.hasQualifiedName("org.junit.jupiter.api", "Assertions", "assertNotNull") or
|
||||
m.hasQualifiedName("org.junit", "Assume", "assumeNotNull") or // vararg
|
||||
m.hasQualifiedName("org.testng", "Assert", "assertNotNull")
|
||||
)
|
||||
or
|
||||
arg = m.getNumberOfParameters() - 1 and
|
||||
(
|
||||
m.hasQualifiedName("org.junit", "Assert", "assertNotNull") or
|
||||
m.hasQualifiedName("junit.framework", _, "assertNotNull")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `m` is a method that checks that its argument at position `arg`
|
||||
* satisfies a property specified by another argument and throws otherwise.
|
||||
*/
|
||||
private predicate methodCheckThat(Method m, int arg) {
|
||||
m.getParameter(arg).getType().getErasure() instanceof TypeObject and
|
||||
(
|
||||
m.hasQualifiedName("org.hamcrest", "MatcherAssert", "assertThat") or
|
||||
m.hasQualifiedName("org.junit", "Assert", "assertThat") or
|
||||
m.hasQualifiedName("org.junit", "Assume", "assumeThat")
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `m` is a method that unconditionally throws. */
|
||||
private predicate methodUnconditionallyThrows(Method m) {
|
||||
m.hasQualifiedName("org.junit.jupiter.api", "Assertions", "fail") or
|
||||
m.hasQualifiedName("org.junit", "Assert", "fail") or
|
||||
m.hasQualifiedName("junit.framework", _, "fail") or
|
||||
m.hasQualifiedName("org.testng", "Assert", "fail")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `mc` is a call to a method that checks that its argument `arg` is
|
||||
* equal to `checkTrue` and throws otherwise.
|
||||
*/
|
||||
predicate methodCallChecksBoolean(MethodCall mc, Expr arg, boolean checkTrue) {
|
||||
exists(int pos | mc.getArgument(pos) = arg |
|
||||
methodCheckTrue(mc.getMethod().getSourceDeclaration(), pos) and checkTrue = true
|
||||
or
|
||||
methodCheckFalse(mc.getMethod().getSourceDeclaration(), pos) and checkTrue = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `mc` is a call to a method that checks that its argument `arg` is
|
||||
* not null and throws otherwise.
|
||||
*/
|
||||
predicate methodCallChecksNotNull(MethodCall mc, Expr arg) {
|
||||
exists(int pos | mc.getArgument(pos) = arg |
|
||||
methodCheckNotNull(mc.getMethod().getSourceDeclaration(), pos)
|
||||
or
|
||||
methodCheckThat(mc.getMethod().getSourceDeclaration(), pos) and
|
||||
mc.getAnArgument().(MethodCall).getMethod().getName() = "notNullValue"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `mc` is a call to a method that checks one of its arguments in some
|
||||
* way and possibly throws.
|
||||
*/
|
||||
predicate methodCallChecksArgument(MethodCall mc) {
|
||||
methodCallChecksBoolean(mc, _, _) or
|
||||
methodCallChecksNotNull(mc, _)
|
||||
}
|
||||
|
||||
/** Holds if `mc` is a call to a method that unconditionally throws. */
|
||||
predicate methodCallUnconditionallyThrows(MethodCall mc) {
|
||||
methodUnconditionallyThrows(mc.getMethod().getSourceDeclaration()) or
|
||||
exists(BooleanLiteral b | methodCallChecksBoolean(mc, b, b.getBooleanValue().booleanNot()))
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `methodCallChecksBoolean` instead.
|
||||
*
|
||||
* Holds if `m` is a non-overridable method that checks that its zero-indexed `argument`
|
||||
* is equal to `checkTrue` and throws otherwise.
|
||||
*/
|
||||
predicate conditionCheckMethodArgument(Method m, int argument, boolean checkTrue) {
|
||||
condtionCheckMethodGooglePreconditions(m, checkTrue) and argument = 0
|
||||
deprecated predicate conditionCheckMethodArgument(Method m, int argument, boolean checkTrue) {
|
||||
methodCheckTrue(m, argument) and checkTrue = true
|
||||
or
|
||||
conditionCheckMethodApacheCommonsLang3Validate(m, checkTrue) and argument = 0
|
||||
or
|
||||
condtionCheckMethodTestingFramework(m, argument, checkTrue)
|
||||
or
|
||||
exists(Parameter p, MethodCall ma, int argIndex, boolean ct, Expr arg |
|
||||
p = m.getParameter(argument) and
|
||||
not m.isOverridable() and
|
||||
m.getBody().getStmt(0).(ExprStmt).getExpr() = ma and
|
||||
conditionCheckArgument(ma, argIndex, ct) and
|
||||
ma.getArgument(argIndex) = arg and
|
||||
(
|
||||
arg.(LogNotExpr).getExpr().(VarAccess).getVariable() = p and
|
||||
checkTrue = ct.booleanNot()
|
||||
or
|
||||
arg.(VarAccess).getVariable() = p and checkTrue = ct
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Parameter p, IfStmt ifstmt, Expr cond |
|
||||
p = m.getParameter(argument) and
|
||||
not m.isOverridable() and
|
||||
p.getType() instanceof BooleanType and
|
||||
m.getBody().getStmt(0) = ifstmt and
|
||||
ifstmt.getCondition() = cond and
|
||||
(
|
||||
cond.(LogNotExpr).getExpr().(VarAccess).getVariable() = p and checkTrue = true
|
||||
or
|
||||
cond.(VarAccess).getVariable() = p and checkTrue = false
|
||||
) and
|
||||
(
|
||||
ifstmt.getThen() instanceof ThrowStmt or
|
||||
ifstmt.getThen().(SingletonBlock).getStmt() instanceof ThrowStmt
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate condtionCheckMethodGooglePreconditions(Method m, boolean checkTrue) {
|
||||
m.getDeclaringType().hasQualifiedName("com.google.common.base", "Preconditions") and
|
||||
checkTrue = true and
|
||||
(m.hasName("checkArgument") or m.hasName("checkState"))
|
||||
}
|
||||
|
||||
private predicate conditionCheckMethodApacheCommonsLang3Validate(Method m, boolean checkTrue) {
|
||||
m.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "Validate") and
|
||||
checkTrue = true and
|
||||
(m.hasName("isTrue") or m.hasName("validState"))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `m` is a non-overridable testing framework method that checks that its first argument
|
||||
* is equal to `checkTrue` and throws otherwise.
|
||||
*/
|
||||
private predicate condtionCheckMethodTestingFramework(Method m, int argument, boolean checkTrue) {
|
||||
argument = 0 and
|
||||
(
|
||||
m.getDeclaringType().hasQualifiedName("org.junit", "Assume") and
|
||||
checkTrue = true and
|
||||
m.hasName("assumeTrue")
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("org.junit.jupiter.api", "Assertions") and
|
||||
(
|
||||
checkTrue = true and m.hasName("assertTrue")
|
||||
or
|
||||
checkTrue = false and m.hasName("assertFalse")
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName("org.junit.jupiter.api", "Assumptions") and
|
||||
(
|
||||
checkTrue = true and m.hasName("assumeTrue")
|
||||
or
|
||||
checkTrue = false and m.hasName("assumeFalse")
|
||||
)
|
||||
)
|
||||
or
|
||||
m.getDeclaringType().hasQualifiedName(["org.junit", "org.testng"], "Assert") and
|
||||
m.getParameter(argument).getType() instanceof BooleanType and
|
||||
(
|
||||
checkTrue = true and m.hasName("assertTrue")
|
||||
or
|
||||
checkTrue = false and m.hasName("assertFalse")
|
||||
)
|
||||
methodCheckFalse(m, argument) and checkTrue = false
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `methodCallChecksBoolean` instead.
|
||||
*
|
||||
* Holds if `ma` is an access to a non-overridable method that checks that its
|
||||
* zero-indexed `argument` is equal to `checkTrue` and throws otherwise.
|
||||
*/
|
||||
predicate conditionCheckArgument(MethodCall ma, int argument, boolean checkTrue) {
|
||||
deprecated predicate conditionCheckArgument(MethodCall ma, int argument, boolean checkTrue) {
|
||||
conditionCheckMethodArgument(ma.getMethod().getSourceDeclaration(), argument, checkTrue)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ private import RangeUtils
|
||||
private import RangeAnalysis
|
||||
|
||||
/** Gets an expression that might have the value `i`. */
|
||||
private Expr exprWithIntValue(int i) {
|
||||
deprecated private Expr exprWithIntValue(int i) {
|
||||
result.(ConstantIntegerExpr).getIntValue() = i or
|
||||
result.(ChooseExpr).getAResultExpr() = exprWithIntValue(i)
|
||||
}
|
||||
@@ -19,11 +19,11 @@ private Expr exprWithIntValue(int i) {
|
||||
* An expression for which the predicate `integerGuard` is relevant.
|
||||
* This includes `VarRead` and `MethodCall`.
|
||||
*/
|
||||
class IntComparableExpr extends Expr {
|
||||
deprecated class IntComparableExpr extends Expr {
|
||||
IntComparableExpr() { this instanceof VarRead or this instanceof MethodCall }
|
||||
|
||||
/** Gets an integer that is directly assigned to the expression in case of a variable; or zero. */
|
||||
int relevantInt() {
|
||||
deprecated int relevantInt() {
|
||||
exists(SsaExplicitUpdate ssa, SsaSourceVariable v |
|
||||
this = v.getAnAccess() and
|
||||
ssa.getSourceVariable() = v and
|
||||
@@ -55,6 +55,9 @@ private predicate comparison(ComparisonExpr comp, boolean branch, Expr e1, Expr
|
||||
* Holds if `guard` evaluating to `branch` ensures that:
|
||||
* `e <= k` when `upper = true`
|
||||
* `e >= k` when `upper = false`
|
||||
*
|
||||
* Does _not_ include the constant comparison case where the guard directly
|
||||
* ensures `e == k`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate rangeGuard(Expr guard, boolean branch, Expr e, int k, boolean upper) {
|
||||
@@ -62,7 +65,8 @@ predicate rangeGuard(Expr guard, boolean branch, Expr e, int k, boolean upper) {
|
||||
eqtest = guard and
|
||||
eqtest.hasOperands(e, c) and
|
||||
bounded(c, any(ZeroBound zb), k, upper, _) and
|
||||
branch = eqtest.polarity()
|
||||
branch = eqtest.polarity() and
|
||||
not c instanceof ConstantIntegerExpr
|
||||
)
|
||||
or
|
||||
exists(Expr c, int val, boolean strict, int d |
|
||||
@@ -87,6 +91,30 @@ predicate rangeGuard(Expr guard, boolean branch, Expr e, int k, boolean upper) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression that directly tests whether a given expression, `e`, is
|
||||
* non-zero.
|
||||
*/
|
||||
Expr nonZeroGuard(Expr e, boolean branch) {
|
||||
exists(EqualityTest eqtest, boolean polarity, int k |
|
||||
eqtest = result and
|
||||
eqtest.hasOperands(e, any(ConstantIntegerExpr c | c.getIntValue() = k)) and
|
||||
polarity = eqtest.polarity()
|
||||
|
|
||||
k = 0 and branch = polarity.booleanNot()
|
||||
or
|
||||
k != 0 and branch = polarity
|
||||
)
|
||||
or
|
||||
exists(int val, boolean upper | rangeGuard(result, branch, e, val, upper) |
|
||||
upper = true and val < 0 // e <= val < 0 ==> e != 0
|
||||
or
|
||||
upper = false and val > 0 // e >= val > 0 ==> e != 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED.
|
||||
*
|
||||
* An expression that directly tests whether a given expression is equal to `k` or not.
|
||||
* The set of `k`s is restricted to those that are relevant for the expression or
|
||||
* have a direct comparison with the expression.
|
||||
@@ -95,7 +123,7 @@ predicate rangeGuard(Expr guard, boolean branch, Expr e, int k, boolean upper) {
|
||||
* is true, and different from `k` if `is_k` is false.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Expr integerGuard(IntComparableExpr e, boolean branch, int k, boolean is_k) {
|
||||
deprecated Expr integerGuard(IntComparableExpr e, boolean branch, int k, boolean is_k) {
|
||||
exists(EqualityTest eqtest, boolean polarity |
|
||||
eqtest = result and
|
||||
eqtest.hasOperands(e, any(ConstantIntegerExpr c | c.getIntValue() = k)) and
|
||||
@@ -119,13 +147,15 @@ Expr integerGuard(IntComparableExpr e, boolean branch, int k, boolean is_k) {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `rangeGuard` instead.
|
||||
*
|
||||
* A guard that splits the values of a variable into one range with an upper bound of `k-1`
|
||||
* and one with a lower bound of `k`.
|
||||
*
|
||||
* If `branch_with_lower_bound_k` is true then `result` is equivalent to `k <= x`
|
||||
* and if it is false then `result` is equivalent to `k > x`.
|
||||
*/
|
||||
Expr intBoundGuard(VarRead x, boolean branch_with_lower_bound_k, int k) {
|
||||
deprecated Expr intBoundGuard(VarRead x, boolean branch_with_lower_bound_k, int k) {
|
||||
exists(ComparisonExpr comp, ConstantIntegerExpr c, int val |
|
||||
comp = result and
|
||||
comp.hasOperands(x, c) and
|
||||
|
||||
@@ -9,49 +9,31 @@
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
/*
|
||||
* Implementation details:
|
||||
*
|
||||
* The three exported predicates, `nullDeref`, `alwaysNullDeref`, and
|
||||
* `superfluousNullGuard`, compute potential null dereferences, definite null
|
||||
* dereferences, and superfluous null checks, respectively. The bulk of the
|
||||
* library supports `nullDeref`, while the latter two are fairly simple in
|
||||
* comparison.
|
||||
*
|
||||
* The NPE (NullPointerException) candidates are computed by
|
||||
* `nullDerefCandidate` and consist of three parts: A variable definition that
|
||||
* might be null as computed by `varMaybeNull`, a dereference that can cause a
|
||||
* NPE as computed by `firstVarDereferenceInBlock`, and a control flow path
|
||||
* between the two points. The path is computed by `varMaybeNullInBlock`,
|
||||
* which is the transitive closure of the step relation `nullVarStep`
|
||||
* originating in a definition given by `varMaybeNull`. The step relation
|
||||
* `nullVarStep` is essentially just the successor relation on basic blocks
|
||||
* restricted to exclude edges along which the variable cannot be null.
|
||||
*
|
||||
* The step relation `nullVarStep` is then reused twice to produce two
|
||||
* refinements of the path reachability predicate `varMaybeNullInBlock` in
|
||||
* order to prune impossible paths that would otherwise lead to a potential
|
||||
* NPE. These two refinements are `varMaybeNullInBlock_corrCond` and
|
||||
* `varMaybeNullInBlock_trackVar` and are described in further detail below.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import SSA
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import RangeUtils
|
||||
private import IntegerGuards
|
||||
private import NullGuards
|
||||
private import semmle.code.java.Collections
|
||||
private import semmle.code.java.frameworks.Assertions
|
||||
private import semmle.code.java.controlflow.internal.Preconditions
|
||||
private import semmle.code.java.controlflow.ControlFlow as Cf
|
||||
|
||||
/** Gets an expression that may be `null`. */
|
||||
Expr nullExpr() {
|
||||
result instanceof NullLiteral or
|
||||
result.(ChooseExpr).getAResultExpr() = nullExpr() or
|
||||
result.(AssignExpr).getSource() = nullExpr() or
|
||||
result.(CastExpr).getExpr() = nullExpr() or
|
||||
result.(ImplicitCastExpr).getExpr() = nullExpr() or
|
||||
result instanceof SafeCastExpr
|
||||
Expr nullExpr() { result = nullExpr(_) }
|
||||
|
||||
/** Gets an expression that may be `null`. */
|
||||
private Expr nullExpr(Expr reason) {
|
||||
result instanceof NullLiteral and reason = result
|
||||
or
|
||||
result.(ChooseExpr).getAResultExpr() = nullExpr(reason)
|
||||
or
|
||||
result.(AssignExpr).getSource() = nullExpr(reason)
|
||||
or
|
||||
result.(CastExpr).getExpr() = nullExpr(reason)
|
||||
or
|
||||
result.(ImplicitCastExpr).getExpr() = nullExpr(reason)
|
||||
or
|
||||
result instanceof SafeCastExpr and reason = result
|
||||
}
|
||||
|
||||
/** An expression of a boxed type that is implicitly unboxed. */
|
||||
@@ -137,58 +119,29 @@ private ControlFlowNode varDereference(SsaVariable v, VarAccess va) {
|
||||
}
|
||||
|
||||
/**
|
||||
* A `ControlFlowNode` that ensures that the SSA variable is not null in any
|
||||
* subsequent use, either by dereferencing it or by an assertion.
|
||||
*/
|
||||
private ControlFlowNode ensureNotNull(SsaVariable v) {
|
||||
result = varDereference(v, _)
|
||||
or
|
||||
exists(AssertTrueMethod m | result.asCall() = m.getACheck(directNullGuard(v, true, false)))
|
||||
or
|
||||
exists(AssertFalseMethod m | result.asCall() = m.getACheck(directNullGuard(v, false, false)))
|
||||
or
|
||||
exists(AssertNotNullMethod m | result.asCall() = m.getACheck(v.getAUse()))
|
||||
or
|
||||
exists(AssertThatMethod m, MethodCall ma |
|
||||
result.asCall() = m.getACheck(v.getAUse()) and ma.getControlFlowNode() = result
|
||||
|
|
||||
ma.getAnArgument().(MethodCall).getMethod().getName() = "notNullValue"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable dereference that cannot be reached by a `null` value, because of an earlier
|
||||
* dereference or assertion in the same `BasicBlock`.
|
||||
*/
|
||||
private predicate unreachableVarDereference(BasicBlock bb, SsaVariable v, ControlFlowNode varDeref) {
|
||||
exists(ControlFlowNode n, int i, int j |
|
||||
(n = ensureNotNull(v) or assertFail(bb, n)) and
|
||||
varDeref = varDereference(v, _) and
|
||||
bb.getNode(i) = n and
|
||||
bb.getNode(j) = varDeref and
|
||||
i < j
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The first dereference of a variable in a given `BasicBlock` excluding those dereferences
|
||||
* that are preceded by a not-null assertion or a trivially failing assertion.
|
||||
* The first dereference of a variable in a given `BasicBlock`.
|
||||
*/
|
||||
private predicate firstVarDereferenceInBlock(BasicBlock bb, SsaVariable v, VarAccess va) {
|
||||
exists(ControlFlowNode n |
|
||||
varDereference(v, va) = n and
|
||||
n.getBasicBlock() = bb and
|
||||
not unreachableVarDereference(bb, v, n)
|
||||
n =
|
||||
min(ControlFlowNode n0, int i |
|
||||
varDereference(v, _) = n0 and bb.getNode(i) = n0
|
||||
|
|
||||
n0 order by i
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** A variable suspected of being `null`. */
|
||||
private predicate varMaybeNull(SsaVariable v, string msg, Expr reason) {
|
||||
private predicate varMaybeNull(SsaVariable v, ControlFlowNode node, string msg, Expr reason) {
|
||||
// A variable compared to null might be null.
|
||||
exists(Expr e |
|
||||
reason = e and
|
||||
msg = "as suggested by $@ null guard" and
|
||||
guardSuggestsVarMaybeNull(e, v) and
|
||||
node = v.getCfgNode() and
|
||||
not v instanceof SsaPhiNode and
|
||||
not clearlyNotNull(v) and
|
||||
// Comparisons in finally blocks are excluded since missing exception edges in the CFG could otherwise yield FPs.
|
||||
@@ -204,6 +157,7 @@ private predicate varMaybeNull(SsaVariable v, string msg, Expr reason) {
|
||||
// A parameter might be null if there is a null argument somewhere.
|
||||
exists(Parameter p, Expr arg |
|
||||
v.(SsaImplicitInit).isParameterDefinition(p) and
|
||||
node = v.getCfgNode() and
|
||||
p.getAnArgument() = arg and
|
||||
reason = arg and
|
||||
msg = "because of $@ null argument" and
|
||||
@@ -214,7 +168,7 @@ private predicate varMaybeNull(SsaVariable v, string msg, Expr reason) {
|
||||
// If the source of a variable is null then the variable may be null.
|
||||
exists(VariableAssign def |
|
||||
v.(SsaExplicitUpdate).getDefiningExpr() = def and
|
||||
def.getSource() = nullExpr() and
|
||||
def.getSource() = nullExpr(node.asExpr()) and
|
||||
reason = def and
|
||||
msg = "because of $@ assignment"
|
||||
)
|
||||
@@ -236,7 +190,7 @@ private Expr nonEmptyExpr() {
|
||||
// ...or it is guarded by a condition proving its length to be non-zero.
|
||||
exists(ConditionBlock cond, boolean branch, FieldAccess length |
|
||||
cond.controls(result.getBasicBlock(), branch) and
|
||||
cond.getCondition() = integerGuard(length, branch, 0, false) and
|
||||
cond.getCondition() = nonZeroGuard(length, branch) and
|
||||
length.getField().hasName("length") and
|
||||
length.getQualifier() = v.getAUse()
|
||||
)
|
||||
@@ -266,7 +220,7 @@ private Expr nonEmptyExpr() {
|
||||
or
|
||||
// ...or a check on its `size`.
|
||||
exists(MethodCall size |
|
||||
c = integerGuard(size, branch, 0, false) and
|
||||
c = nonZeroGuard(size, branch) and
|
||||
size.getMethod().hasName("size") and
|
||||
size.getQualifier() = v.getAUse()
|
||||
)
|
||||
@@ -294,536 +248,29 @@ private predicate impossibleEdge(BasicBlock bb1, BasicBlock bb2) {
|
||||
)
|
||||
}
|
||||
|
||||
/** A control flow edge that leaves a finally-block. */
|
||||
private predicate leavingFinally(BasicBlock bb1, BasicBlock bb2, boolean normaledge) {
|
||||
exists(TryStmt try, BlockStmt finally |
|
||||
try.getFinally() = finally and
|
||||
bb1.getASuccessor() = bb2 and
|
||||
bb1.getFirstNode().getEnclosingStmt().getEnclosingStmt*() = finally and
|
||||
not bb2.getFirstNode().getEnclosingStmt().getEnclosingStmt*() = finally and
|
||||
if bb1.getLastNode().getANormalSuccessor() = bb2.getFirstNode()
|
||||
then normaledge = true
|
||||
else normaledge = false
|
||||
)
|
||||
private module NullnessConfig implements Cf::ControlFlow::ConfigSig {
|
||||
predicate source(ControlFlowNode node, SsaVariable def) { varMaybeNull(def, node, _, _) }
|
||||
|
||||
predicate sink(ControlFlowNode node, SsaVariable def) { varDereference(def, _) = node }
|
||||
|
||||
predicate barrierValue(GuardValue gv) { gv.isNullness(false) }
|
||||
|
||||
predicate barrierEdge(BasicBlock bb1, BasicBlock bb2) { impossibleEdge(bb1, bb2) }
|
||||
|
||||
predicate uncertainFlow() { none() }
|
||||
}
|
||||
|
||||
private predicate ssaSourceVarMaybeNull(SsaSourceVariable v) {
|
||||
varMaybeNull(v.getAnSsaVariable(), _, _)
|
||||
}
|
||||
private module NullnessFlow = Cf::ControlFlow::Flow<NullnessConfig>;
|
||||
|
||||
/**
|
||||
* The step relation for propagating that a given SSA variable might be `null` in a given `BasicBlock`.
|
||||
*
|
||||
* If `midssa` is null in `mid` then `ssa` might be null in `bb`. The SSA variables share the same
|
||||
* `SsaSourceVariable`.
|
||||
*
|
||||
* A boolean flag tracks whether a non-normal completion is waiting to resume upon the exit of a finally-block.
|
||||
* If the flag is set, then the normal edge out of the finally-block is prohibited, but if it is not set then
|
||||
* no knowledge is assumed of any potentially waiting completions. `midstoredcompletion` is the flag before
|
||||
* the step and `storedcompletion` is the flag after the step.
|
||||
*/
|
||||
private predicate nullVarStep(
|
||||
SsaVariable midssa, BasicBlock mid, boolean midstoredcompletion, SsaVariable ssa, BasicBlock bb,
|
||||
boolean storedcompletion
|
||||
) {
|
||||
exists(SsaSourceVariable v |
|
||||
ssaSourceVarMaybeNull(v) and
|
||||
midssa.getSourceVariable() = v
|
||||
|
|
||||
ssa.(SsaPhiNode).getAPhiInput() = midssa and ssa.getBasicBlock() = bb
|
||||
or
|
||||
ssa = midssa and
|
||||
not exists(SsaPhiNode phi | phi.getSourceVariable() = v and phi.getBasicBlock() = bb)
|
||||
) and
|
||||
(midstoredcompletion = true or midstoredcompletion = false) and
|
||||
midssa.isLiveAtEndOfBlock(mid) and
|
||||
not ensureNotNull(midssa).getBasicBlock() = mid and
|
||||
not assertFail(mid, _) and
|
||||
bb = mid.getASuccessor() and
|
||||
not impossibleEdge(mid, bb) and
|
||||
not nullGuardControlsBranchEdge(midssa, false, mid, bb) and
|
||||
not (leavingFinally(mid, bb, true) and midstoredcompletion = true) and
|
||||
if bb.getFirstNode().asStmt() = any(TryStmt try | | try.getFinally())
|
||||
then
|
||||
if bb.getFirstNode() = mid.getLastNode().getANormalSuccessor()
|
||||
then storedcompletion = false
|
||||
else storedcompletion = true
|
||||
else
|
||||
if leavingFinally(mid, bb, _)
|
||||
then storedcompletion = false
|
||||
else storedcompletion = midstoredcompletion
|
||||
}
|
||||
|
||||
/**
|
||||
* The transitive closure of `nullVarStep` originating from `varMaybeNull`. That is, those `BasicBlock`s
|
||||
* for which the SSA variable is suspected of being `null`.
|
||||
*/
|
||||
private predicate varMaybeNullInBlock(
|
||||
SsaVariable ssa, SsaSourceVariable v, BasicBlock bb, boolean storedcompletion
|
||||
) {
|
||||
varMaybeNull(ssa, _, _) and
|
||||
bb = ssa.getBasicBlock() and
|
||||
storedcompletion = false and
|
||||
v = ssa.getSourceVariable()
|
||||
or
|
||||
exists(BasicBlock mid, SsaVariable midssa, boolean midstoredcompletion |
|
||||
varMaybeNullInBlock(midssa, v, mid, midstoredcompletion) and
|
||||
nullVarStep(midssa, mid, midstoredcompletion, ssa, bb, storedcompletion)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is a source variable that might reach a potential `null`
|
||||
* dereference.
|
||||
*/
|
||||
private predicate nullDerefCandidateVariable(SsaSourceVariable v) {
|
||||
exists(SsaVariable ssa, BasicBlock bb |
|
||||
firstVarDereferenceInBlock(bb, ssa, _) and
|
||||
varMaybeNullInBlock(ssa, v, bb, _)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate varMaybeNullInBlock_origin(
|
||||
SsaVariable origin, SsaVariable ssa, BasicBlock bb, boolean storedcompletion
|
||||
) {
|
||||
nullDerefCandidateVariable(ssa.getSourceVariable()) and
|
||||
varMaybeNull(ssa, _, _) and
|
||||
bb = ssa.getBasicBlock() and
|
||||
storedcompletion = false and
|
||||
origin = ssa
|
||||
or
|
||||
exists(BasicBlock mid, SsaVariable midssa, boolean midstoredcompletion |
|
||||
varMaybeNullInBlock_origin(origin, midssa, mid, midstoredcompletion) and
|
||||
nullVarStep(midssa, mid, midstoredcompletion, ssa, bb, storedcompletion)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A potential `null` dereference. That is, the first dereference of a variable in a block
|
||||
* where it is suspected of being `null`.
|
||||
*/
|
||||
private predicate nullDerefCandidate(SsaVariable origin, VarAccess va) {
|
||||
exists(SsaVariable ssa, BasicBlock bb |
|
||||
firstVarDereferenceInBlock(bb, ssa, va) and
|
||||
varMaybeNullInBlock_origin(origin, ssa, bb, _)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* In the following, the potential `null` dereference candidates are pruned by proving that
|
||||
* a `NullPointerException` (NPE) cannot occur. This is done by pruning the control-flow paths
|
||||
* that lead to the NPE candidate in two ways:
|
||||
*
|
||||
* 1. For each set of correlated conditions that are passed by the path, consistent
|
||||
* branches must be taken. For example, the following code is safe due to the two tests on
|
||||
* `flag` begin correlated.
|
||||
* ```
|
||||
* x = null;
|
||||
* if (flag) x = new A();
|
||||
* if (flag) x.m();
|
||||
* ```
|
||||
*
|
||||
* 2. For each other variable that changes its value alongside the potential NPE candidate,
|
||||
* the passed conditions must be consistent with its value. For example, the following
|
||||
* code is safe due to the value of `t`.
|
||||
* ```
|
||||
* x = null;
|
||||
* t = null;
|
||||
* if (...) { x = new A(); t = new B(); }
|
||||
* if (t != null) x.m();
|
||||
* ```
|
||||
* We call such a variable a _tracking variable_ as it tracks the null-ness of `x`.
|
||||
*/
|
||||
|
||||
/** A variable that is assigned `null` if the given condition takes the given branch. */
|
||||
private predicate varConditionallyNull(SsaExplicitUpdate v, ConditionBlock cond, boolean branch) {
|
||||
exists(ConditionalExpr condexpr |
|
||||
v.getDefiningExpr().(VariableAssign).getSource() = condexpr and
|
||||
condexpr.getCondition() = cond.getCondition()
|
||||
|
|
||||
condexpr.getBranchExpr(branch) = nullExpr() and
|
||||
not condexpr.getBranchExpr(branch.booleanNot()) = nullExpr()
|
||||
)
|
||||
or
|
||||
v.getDefiningExpr().(VariableAssign).getSource() = nullExpr() and
|
||||
cond.controls(v.getBasicBlock(), branch)
|
||||
}
|
||||
|
||||
/**
|
||||
* A condition that might be useful in proving an NPE candidate safe.
|
||||
*
|
||||
* This is a condition along the path that found the NPE candidate.
|
||||
*/
|
||||
private predicate interestingCond(SsaSourceVariable npecand, ConditionBlock cond) {
|
||||
nullDerefCandidateVariable(npecand) and
|
||||
(
|
||||
varMaybeNullInBlock(_, npecand, cond, _) or
|
||||
varConditionallyNull(npecand.getAnSsaVariable(), cond, _)
|
||||
) and
|
||||
not cond.getCondition().(Expr).getAChildExpr*() = npecand.getAnAccess()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ConditionBlock ssaIntegerGuard(SsaVariable v, boolean branch, int k, boolean is_k) {
|
||||
result.getCondition() = integerGuard(v.getAUse(), branch, k, is_k)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ConditionBlock ssaIntBoundGuard(SsaVariable v, boolean branch_with_lower_bound_k, int k) {
|
||||
result.getCondition() = intBoundGuard(v.getAUse(), branch_with_lower_bound_k, k)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ConditionBlock ssaEnumConstEquality(SsaVariable v, boolean polarity, EnumConstant c) {
|
||||
result.getCondition() = enumConstEquality(v.getAUse(), polarity, c)
|
||||
}
|
||||
|
||||
private predicate conditionChecksNull(ConditionBlock cond, SsaVariable v, boolean branchIsNull) {
|
||||
nullGuardControlsBranchEdge(v, true, cond, cond.getTestSuccessor(branchIsNull)) and
|
||||
nullGuardControlsBranchEdge(v, false, cond, cond.getTestSuccessor(branchIsNull.booleanNot()))
|
||||
}
|
||||
|
||||
/** A pair of correlated conditions for a given NPE candidate. */
|
||||
private predicate correlatedConditions(
|
||||
SsaSourceVariable npecand, ConditionBlock cond1, ConditionBlock cond2, boolean inverted
|
||||
) {
|
||||
interestingCond(npecand, cond1) and
|
||||
interestingCond(npecand, cond2) and
|
||||
cond1 != cond2 and
|
||||
(
|
||||
exists(SsaVariable v |
|
||||
cond1.getCondition() = v.getAUse() and
|
||||
cond2.getCondition() = v.getAUse() and
|
||||
inverted = false
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, boolean branch1, boolean branch2 |
|
||||
conditionChecksNull(cond1, v, branch1) and
|
||||
conditionChecksNull(cond2, v, branch2) and
|
||||
inverted = branch1.booleanXor(branch2)
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, int k, boolean branch1, boolean branch2 |
|
||||
cond1 = ssaIntegerGuard(v, branch1, k, true) and
|
||||
cond1 = ssaIntegerGuard(v, branch1.booleanNot(), k, false) and
|
||||
cond2 = ssaIntegerGuard(v, branch2, k, true) and
|
||||
cond2 = ssaIntegerGuard(v, branch2.booleanNot(), k, false) and
|
||||
inverted = branch1.booleanXor(branch2)
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, int k, boolean branch1, boolean branch2 |
|
||||
cond1 = ssaIntBoundGuard(v, branch1, k) and
|
||||
cond2 = ssaIntBoundGuard(v, branch2, k) and
|
||||
inverted = branch1.booleanXor(branch2)
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, EnumConstant c, boolean pol1, boolean pol2 |
|
||||
cond1 = ssaEnumConstEquality(v, pol1, c) and
|
||||
cond2 = ssaEnumConstEquality(v, pol2, c) and
|
||||
inverted = pol1.booleanXor(pol2)
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v, RefType type |
|
||||
cond1.getCondition() = instanceofExpr(v, type) and
|
||||
cond2.getCondition() = instanceofExpr(v, type) and
|
||||
inverted = false
|
||||
)
|
||||
or
|
||||
exists(SsaVariable v1, SsaVariable v2, boolean branch1, boolean branch2 |
|
||||
cond1.getCondition() = varEqualityTestExpr(v1, v2, branch1) and
|
||||
cond2.getCondition() = varEqualityTestExpr(v1, v2, branch2) and
|
||||
inverted = branch1.booleanXor(branch2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is again the transitive closure of `nullVarStep` similarly to `varMaybeNullInBlock`, but
|
||||
* this time restricted based on pairs of correlated conditions consistent with `cond1`
|
||||
* evaluating to `branch`.
|
||||
*/
|
||||
private predicate varMaybeNullInBlock_corrCond(
|
||||
SsaVariable origin, SsaVariable ssa, BasicBlock bb, boolean storedcompletion,
|
||||
ConditionBlock cond1, boolean branch
|
||||
) {
|
||||
exists(SsaSourceVariable npecand | npecand = ssa.getSourceVariable() |
|
||||
nullDerefCandidateVariable(npecand) and correlatedConditions(npecand, cond1, _, _)
|
||||
) and
|
||||
(
|
||||
varConditionallyNull(ssa, cond1, branch)
|
||||
or
|
||||
not varConditionallyNull(ssa, cond1, _) and
|
||||
(branch = true or branch = false)
|
||||
) and
|
||||
varMaybeNull(ssa, _, _) and
|
||||
bb = ssa.getBasicBlock() and
|
||||
storedcompletion = false and
|
||||
origin = ssa
|
||||
or
|
||||
exists(BasicBlock mid, SsaVariable midssa, boolean midstoredcompletion |
|
||||
varMaybeNullInBlock_corrCond(origin, midssa, mid, midstoredcompletion, cond1, branch) and
|
||||
(
|
||||
cond1 = mid and cond1.getTestSuccessor(branch) = bb
|
||||
or
|
||||
exists(ConditionBlock cond2, boolean inverted, boolean branch2 |
|
||||
cond2 = mid and
|
||||
correlatedConditions(_, cond1, cond2, inverted) and
|
||||
cond2.getTestSuccessor(branch2) = bb and
|
||||
branch = branch2.booleanXor(inverted)
|
||||
)
|
||||
or
|
||||
cond1 != mid and
|
||||
not exists(ConditionBlock cond2 | cond2 = mid and correlatedConditions(_, cond1, cond2, _))
|
||||
) and
|
||||
nullVarStep(midssa, mid, midstoredcompletion, ssa, bb, storedcompletion)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* A tracking variable has its possible values divided into two sets, A and B, for
|
||||
* which we can attribute at least one direct assignment to be contained in either
|
||||
* A or B.
|
||||
* Four kinds are supported:
|
||||
* - null: A means null and B means non-null.
|
||||
* - boolean: A means true and B means false.
|
||||
* - enum: A means a specific enum constant and B means any other value.
|
||||
* - int: A means a specific integer value and B means any other value.
|
||||
*/
|
||||
|
||||
private newtype TrackVarKind =
|
||||
TrackVarKindNull() or
|
||||
TrackVarKindBool() or
|
||||
TrackVarKindEnum() or
|
||||
TrackVarKindInt()
|
||||
|
||||
/** A variable that might be relevant as a tracking variable for the NPE candidate. */
|
||||
private predicate trackingVar(
|
||||
SsaSourceVariable npecand, SsaExplicitUpdate trackssa, SsaSourceVariable trackvar,
|
||||
TrackVarKind kind, Expr init
|
||||
) {
|
||||
exists(ConditionBlock cond |
|
||||
interestingCond(npecand, cond) and
|
||||
varMaybeNullInBlock(_, npecand, cond, _) and
|
||||
cond.getCondition().(Expr).getAChildExpr*() = trackvar.getAnAccess() and
|
||||
trackssa.getSourceVariable() = trackvar and
|
||||
trackssa.getDefiningExpr().(VariableAssign).getSource() = init
|
||||
|
|
||||
init instanceof NullLiteral and kind = TrackVarKindNull()
|
||||
or
|
||||
init = clearlyNotNullExpr() and kind = TrackVarKindNull()
|
||||
or
|
||||
init instanceof BooleanLiteral and kind = TrackVarKindBool()
|
||||
or
|
||||
init.(VarAccess).getVariable() instanceof EnumConstant and kind = TrackVarKindEnum()
|
||||
or
|
||||
exists(init.(ConstantIntegerExpr).getIntValue()) and kind = TrackVarKindInt()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets an expression that tests the value of a given tracking variable. */
|
||||
private Expr trackingVarGuard(
|
||||
SsaVariable trackssa, SsaSourceVariable trackvar, TrackVarKind kind, boolean branch, boolean isA
|
||||
) {
|
||||
exists(Expr init | trackingVar(_, trackssa, trackvar, kind, init) |
|
||||
result = basicNullGuard(trackvar.getAnAccess(), branch, isA) and
|
||||
kind = TrackVarKindNull()
|
||||
or
|
||||
result = trackvar.getAnAccess() and
|
||||
kind = TrackVarKindBool() and
|
||||
(branch = true or branch = false) and
|
||||
isA = branch
|
||||
or
|
||||
exists(boolean polarity, EnumConstant c, EnumConstant initc |
|
||||
initc.getAnAccess() = init and
|
||||
kind = TrackVarKindEnum() and
|
||||
result = enumConstEquality(trackvar.getAnAccess(), polarity, c) and
|
||||
(
|
||||
initc = c and branch = polarity.booleanNot() and isA = false
|
||||
or
|
||||
initc = c and branch = polarity and isA = true
|
||||
or
|
||||
initc != c and branch = polarity and isA = false
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(int k |
|
||||
init.(ConstantIntegerExpr).getIntValue() = k and
|
||||
kind = TrackVarKindInt()
|
||||
|
|
||||
result = integerGuard(trackvar.getAnAccess(), branch, k, isA)
|
||||
or
|
||||
exists(int k2 |
|
||||
result = integerGuard(trackvar.getAnAccess(), branch.booleanNot(), k2, true) and
|
||||
isA = false and
|
||||
k2 != k
|
||||
)
|
||||
or
|
||||
exists(int bound, boolean branch_with_lower_bound |
|
||||
result = intBoundGuard(trackvar.getAnAccess(), branch_with_lower_bound, bound) and
|
||||
isA = false
|
||||
|
|
||||
branch = branch_with_lower_bound and k < bound
|
||||
or
|
||||
branch = branch_with_lower_bound.booleanNot() and bound <= k
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(EqualityTest eqtest, boolean branch0, boolean polarity, BooleanLiteral boollit |
|
||||
eqtest = result and
|
||||
eqtest.hasOperands(trackingVarGuard(trackssa, trackvar, kind, branch0, isA), boollit) and
|
||||
eqtest.polarity() = polarity and
|
||||
branch = branch0.booleanXor(polarity).booleanXor(boollit.getBooleanValue())
|
||||
)
|
||||
}
|
||||
|
||||
/** An update to a tracking variable that is contained fully in either A or B. */
|
||||
private predicate isReset(
|
||||
SsaVariable trackssa, SsaSourceVariable trackvar, TrackVarKind kind, SsaExplicitUpdate update,
|
||||
boolean isA
|
||||
) {
|
||||
exists(Expr init, Expr e |
|
||||
trackingVar(_, trackssa, trackvar, kind, init) and
|
||||
update.getSourceVariable() = trackvar and
|
||||
e = update.getDefiningExpr().(VariableAssign).getSource()
|
||||
|
|
||||
e instanceof NullLiteral and kind = TrackVarKindNull() and isA = true
|
||||
or
|
||||
e = clearlyNotNullExpr() and kind = TrackVarKindNull() and isA = false
|
||||
or
|
||||
e.(BooleanLiteral).getBooleanValue() = isA and kind = TrackVarKindBool()
|
||||
or
|
||||
e.(VarAccess).getVariable().(EnumConstant) = init.(VarAccess).getVariable() and
|
||||
kind = TrackVarKindEnum() and
|
||||
isA = true
|
||||
or
|
||||
e.(VarAccess).getVariable().(EnumConstant) != init.(VarAccess).getVariable() and
|
||||
kind = TrackVarKindEnum() and
|
||||
isA = false
|
||||
or
|
||||
e.(ConstantIntegerExpr).getIntValue() = init.(ConstantIntegerExpr).getIntValue() and
|
||||
kind = TrackVarKindInt() and
|
||||
isA = true
|
||||
or
|
||||
e.(ConstantIntegerExpr).getIntValue() != init.(ConstantIntegerExpr).getIntValue() and
|
||||
kind = TrackVarKindInt() and
|
||||
isA = false
|
||||
)
|
||||
}
|
||||
|
||||
/** The abstract value of the tracked variable. */
|
||||
private newtype TrackedValue =
|
||||
TrackedValueA() or
|
||||
TrackedValueB() or
|
||||
TrackedValueUnknown()
|
||||
|
||||
private TrackedValue trackValAorB(boolean isA) {
|
||||
isA = true and result = TrackedValueA()
|
||||
or
|
||||
isA = false and result = TrackedValueB()
|
||||
}
|
||||
|
||||
/** A control flow edge passing through a condition that implies a specific value for a tracking variable. */
|
||||
private predicate stepImplies(
|
||||
BasicBlock bb1, BasicBlock bb2, SsaVariable trackssa, SsaSourceVariable trackvar,
|
||||
TrackVarKind kind, boolean isA
|
||||
) {
|
||||
exists(ConditionBlock cond, boolean branch |
|
||||
cond = bb1 and
|
||||
cond.getTestSuccessor(branch) = bb2 and
|
||||
cond.getCondition() = trackingVarGuard(trackssa, trackvar, kind, branch, isA)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This is again the transitive closure of `nullVarStep` similarly to `varMaybeNullInBlock`, but
|
||||
* this time restricted based on a tracking variable.
|
||||
*/
|
||||
private predicate varMaybeNullInBlock_trackVar(
|
||||
SsaVariable origin, SsaVariable ssa, BasicBlock bb, boolean storedcompletion,
|
||||
SsaVariable trackssa, SsaSourceVariable trackvar, TrackVarKind kind, TrackedValue trackvalue
|
||||
) {
|
||||
exists(SsaSourceVariable npecand | npecand = ssa.getSourceVariable() |
|
||||
nullDerefCandidateVariable(npecand) and trackingVar(npecand, trackssa, trackvar, kind, _)
|
||||
) and
|
||||
(
|
||||
exists(SsaVariable init, boolean isA |
|
||||
init.getSourceVariable() = trackvar and
|
||||
init.isLiveAtEndOfBlock(bb) and
|
||||
isReset(trackssa, trackvar, kind, init, isA) and
|
||||
trackvalue = trackValAorB(isA)
|
||||
)
|
||||
or
|
||||
trackvalue = TrackedValueUnknown() and
|
||||
not exists(SsaVariable init |
|
||||
init.getSourceVariable() = trackvar and
|
||||
init.isLiveAtEndOfBlock(bb) and
|
||||
isReset(trackssa, trackvar, kind, init, _)
|
||||
)
|
||||
) and
|
||||
varMaybeNull(ssa, _, _) and
|
||||
bb = ssa.getBasicBlock() and
|
||||
storedcompletion = false and
|
||||
origin = ssa
|
||||
or
|
||||
exists(BasicBlock mid, SsaVariable midssa, boolean midstoredcompletion, TrackedValue trackvalue0 |
|
||||
varMaybeNullInBlock_trackVar(origin, midssa, mid, midstoredcompletion, trackssa, trackvar, kind,
|
||||
trackvalue0) and
|
||||
nullVarStep(midssa, mid, midstoredcompletion, ssa, bb, storedcompletion) and
|
||||
(
|
||||
trackvalue0 = TrackedValueUnknown()
|
||||
or
|
||||
// A step that implies a value that contradicts the current value is not allowed.
|
||||
exists(boolean isA | trackvalue0 = trackValAorB(isA) |
|
||||
not stepImplies(mid, bb, trackssa, trackvar, kind, isA.booleanNot())
|
||||
)
|
||||
) and
|
||||
(
|
||||
// If no update occurs then the tracked value is unchanged unless the step implies a given value via a condition.
|
||||
not exists(SsaUpdate update |
|
||||
update.getSourceVariable() = trackvar and
|
||||
update.getBasicBlock() = bb
|
||||
) and
|
||||
(
|
||||
exists(boolean isA | stepImplies(mid, bb, trackssa, trackvar, kind, isA) |
|
||||
trackvalue = trackValAorB(isA)
|
||||
)
|
||||
or
|
||||
not stepImplies(mid, bb, trackssa, trackvar, kind, _) and trackvalue = trackvalue0
|
||||
)
|
||||
or
|
||||
// If an update occurs then the tracked value is set accordingly.
|
||||
exists(SsaUpdate update |
|
||||
update.getSourceVariable() = trackvar and
|
||||
update.getBasicBlock() = bb
|
||||
|
|
||||
exists(boolean isA | isReset(trackssa, trackvar, kind, update, isA) |
|
||||
trackvalue = trackValAorB(isA)
|
||||
)
|
||||
or
|
||||
not isReset(trackssa, trackvar, kind, update, _) and trackvalue = TrackedValueUnknown()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A potential `null` dereference that has not been proven safe.
|
||||
* Holds if the dereference of `v` at `va` might be `null`.
|
||||
*/
|
||||
predicate nullDeref(SsaSourceVariable v, VarAccess va, string msg, Expr reason) {
|
||||
exists(SsaVariable origin, SsaVariable ssa, BasicBlock bb |
|
||||
nullDerefCandidate(origin, va) and
|
||||
varMaybeNull(origin, msg, reason) and
|
||||
exists(SsaVariable origin, SsaVariable ssa, ControlFlowNode src, ControlFlowNode sink |
|
||||
varMaybeNull(origin, src, msg, reason) and
|
||||
NullnessFlow::flow(src, origin, sink, ssa) and
|
||||
ssa.getSourceVariable() = v and
|
||||
firstVarDereferenceInBlock(bb, ssa, va) and
|
||||
forall(ConditionBlock cond | correlatedConditions(v, cond, _, _) |
|
||||
varMaybeNullInBlock_corrCond(origin, ssa, bb, _, cond, _)
|
||||
) and
|
||||
forall(SsaVariable guardssa, SsaSourceVariable guardvar, TrackVarKind kind |
|
||||
trackingVar(v, guardssa, guardvar, kind, _)
|
||||
|
|
||||
varMaybeNullInBlock_trackVar(origin, ssa, bb, _, guardssa, guardvar, kind, _)
|
||||
)
|
||||
varDereference(ssa, va) = sink
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -253,17 +253,18 @@ class SsaImplicitUpdate extends SsaUpdate {
|
||||
or
|
||||
if this.hasImplicitQualifierUpdate()
|
||||
then
|
||||
if exists(this.getANonLocalUpdate())
|
||||
if isNonLocal(this)
|
||||
then result = "nonlocal + nonlocal qualifier"
|
||||
else result = "nonlocal qualifier"
|
||||
else (
|
||||
exists(this.getANonLocalUpdate()) and result = "nonlocal"
|
||||
isNonLocal(this) and result = "nonlocal"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a reachable `FieldWrite` that might represent this ssa update, if any.
|
||||
*/
|
||||
overlay[global]
|
||||
FieldWrite getANonLocalUpdate() {
|
||||
exists(SsaSourceField f, Callable setter |
|
||||
relevantFieldUpdate(setter, f.getField(), result) and
|
||||
@@ -287,6 +288,11 @@ class SsaImplicitUpdate extends SsaUpdate {
|
||||
}
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private predicate isNonLocalImpl(SsaImplicitUpdate su) { exists(su.getANonLocalUpdate()) }
|
||||
|
||||
private predicate isNonLocal(SsaImplicitUpdate su) = forceLocal(isNonLocalImpl/1)(su)
|
||||
|
||||
/**
|
||||
* An SSA variable that represents an uncertain implicit update of the value.
|
||||
* This is a `Call` that might reach a non-local update of the field or one of
|
||||
|
||||
@@ -157,17 +157,7 @@ private module BaseSsaImpl {
|
||||
|
||||
private import BaseSsaImpl
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||
private import java as J
|
||||
|
||||
class BasicBlock = J::BasicBlock;
|
||||
|
||||
class ControlFlowNode = J::ControlFlowNode;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock> {
|
||||
class SourceVariable = BaseSsaSourceVariable;
|
||||
|
||||
/**
|
||||
@@ -199,7 +189,7 @@ private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||
}
|
||||
}
|
||||
|
||||
private module Impl = SsaImplCommon::Make<Location, SsaInput>;
|
||||
private module Impl = SsaImplCommon::Make<Location, Cfg, SsaInput>;
|
||||
|
||||
private import Cached
|
||||
|
||||
|
||||
@@ -470,7 +470,8 @@ private predicate enhancedForStmtStep(Node node1, Node node2, Type containerType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` reads a value from an array.
|
||||
* Holds if the step from `node1` to `node2` reads a value from an array, where
|
||||
* the elements are of type `elemType`.
|
||||
* This covers ordinary array reads as well as array iteration through enhanced
|
||||
* `for` statements.
|
||||
*/
|
||||
|
||||
@@ -69,28 +69,10 @@ private predicate closureFlowStep(Expr e1, Expr e2) {
|
||||
)
|
||||
}
|
||||
|
||||
private module CaptureInput implements VariableCapture::InputSig<Location> {
|
||||
private module CaptureInput implements VariableCapture::InputSig<Location, BasicBlock> {
|
||||
private import java as J
|
||||
|
||||
class BasicBlock instanceof J::BasicBlock {
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
ControlFlowNode getNode(int i) { result = super.getNode(i) }
|
||||
|
||||
int length() { result = super.length() }
|
||||
|
||||
Callable getEnclosingCallable() { result = super.getEnclosingCallable() }
|
||||
|
||||
Location getLocation() { result = super.getLocation() }
|
||||
}
|
||||
|
||||
class ControlFlowNode = J::ControlFlowNode;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) {
|
||||
result.(J::BasicBlock).immediatelyDominates(bb)
|
||||
}
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.(J::BasicBlock).getASuccessor() }
|
||||
Callable basicBlockGetEnclosingCallable(BasicBlock bb) { result = bb.getEnclosingCallable() }
|
||||
|
||||
//TODO: support capture of `this` in lambdas
|
||||
class CapturedVariable instanceof LocalScopeVariable {
|
||||
@@ -165,7 +147,7 @@ class CapturedVariable = CaptureInput::CapturedVariable;
|
||||
|
||||
class CapturedParameter = CaptureInput::CapturedParameter;
|
||||
|
||||
module CaptureFlow = VariableCapture::Flow<Location, CaptureInput>;
|
||||
module CaptureFlow = VariableCapture::Flow<Location, Cfg, CaptureInput>;
|
||||
|
||||
private CaptureFlow::ClosureNode asClosureNode(Node n) {
|
||||
result = n.(CaptureNode).getSynthesizedCaptureNode()
|
||||
|
||||
@@ -83,6 +83,7 @@ overlay[caller?]
|
||||
pragma[inline]
|
||||
predicate localFlow(Node node1, Node node2) { node1 = node2 or localFlowStepPlus(node1, node2) }
|
||||
|
||||
overlay[caller?]
|
||||
private predicate localFlowStepPlus(Node node1, Node node2) = fastTC(localFlowStep/2)(node1, node2)
|
||||
|
||||
/**
|
||||
@@ -263,8 +264,8 @@ class Content extends TContent {
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* The location spans column `sc` of line `sl` to
|
||||
* column `ec` of line `el` in file `path`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
@@ -362,8 +363,8 @@ class ContentSet instanceof Content {
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* The location spans column `sc` of line `sl` to
|
||||
* column `ec` of line `el` in file `path`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
|
||||
@@ -132,7 +132,7 @@ private module TypesInput implements Impl::Private::TypesInputSig {
|
||||
exists(rk)
|
||||
}
|
||||
|
||||
DataFlowType getSourceType(Input::SourceBase source, Impl::Private::SummaryComponent sc) {
|
||||
DataFlowType getSourceType(Input::SourceBase source, Impl::Private::SummaryComponentStack s) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -144,7 +144,9 @@ private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
sc = viableCallable(result).asSummarizedCallable()
|
||||
}
|
||||
|
||||
Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponent sc) { none() }
|
||||
DataFlowCallable getSourceNodeEnclosingCallable(Input::SourceBase source) { none() }
|
||||
|
||||
Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponentStack s) { none() }
|
||||
|
||||
Node getSinkNode(Input::SinkBase sink, Impl::Private::SummaryComponent sc) { none() }
|
||||
}
|
||||
|
||||
@@ -157,26 +157,21 @@ private predicate hasEntryDef(TrackedVar v, BasicBlock b) {
|
||||
}
|
||||
|
||||
/** Holds if `n` might update the locally tracked variable `v`. */
|
||||
overlay[global]
|
||||
pragma[nomagic]
|
||||
private predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
private predicate uncertainVariableUpdateImpl(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) {
|
||||
exists(Call c | c = n.asCall() | updatesNamedField(c, v, _)) and
|
||||
b.getNode(i) = n and
|
||||
hasDominanceInformation(b)
|
||||
or
|
||||
uncertainVariableUpdate(v.getQualifier(), n, b, i)
|
||||
uncertainVariableUpdateImpl(v.getQualifier(), n, b, i)
|
||||
}
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||
private import java as J
|
||||
|
||||
class BasicBlock = J::BasicBlock;
|
||||
|
||||
class ControlFlowNode = J::ControlFlowNode;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
/** Holds if `n` might update the locally tracked variable `v`. */
|
||||
predicate uncertainVariableUpdate(TrackedVar v, ControlFlowNode n, BasicBlock b, int i) =
|
||||
forceLocal(uncertainVariableUpdateImpl/4)(v, n, b, i)
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location, BasicBlock> {
|
||||
class SourceVariable = SsaSourceVariable;
|
||||
|
||||
/**
|
||||
@@ -218,7 +213,7 @@ private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||
}
|
||||
}
|
||||
|
||||
import SsaImplCommon::Make<Location, SsaInput> as Impl
|
||||
import SsaImplCommon::Make<Location, Cfg, SsaInput> as Impl
|
||||
|
||||
final class Definition = Impl::Definition;
|
||||
|
||||
@@ -345,6 +340,7 @@ private module Cached {
|
||||
* Constructor --(intraInstanceCallEdge)-->+ Method(setter of this.f)
|
||||
* ```
|
||||
*/
|
||||
overlay[global]
|
||||
private predicate intraInstanceCallEdge(Callable c1, Method m2) {
|
||||
exists(MethodCall ma, RefType t1 |
|
||||
ma.getCaller() = c1 and
|
||||
@@ -365,6 +361,7 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private Callable tgt(Call c) {
|
||||
result = viableImpl_v2(c)
|
||||
or
|
||||
@@ -374,11 +371,13 @@ private module Cached {
|
||||
}
|
||||
|
||||
/** Holds if `(c1,c2)` is an edge in the call graph. */
|
||||
overlay[global]
|
||||
private predicate callEdge(Callable c1, Callable c2) {
|
||||
exists(Call c | c.getCaller() = c1 and c2 = tgt(c))
|
||||
}
|
||||
|
||||
/** Holds if `(c1,c2)` is an edge in the call graph excluding `intraInstanceCallEdge`. */
|
||||
overlay[global]
|
||||
private predicate crossInstanceCallEdge(Callable c1, Callable c2) {
|
||||
callEdge(c1, c2) and not intraInstanceCallEdge(c1, c2)
|
||||
}
|
||||
@@ -392,6 +391,7 @@ private module Cached {
|
||||
relevantFieldUpdate(_, f.getField(), _)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private predicate source(Call call, TrackedField f, Field field, Callable c, boolean fresh) {
|
||||
relevantCall(call, f) and
|
||||
field = f.getField() and
|
||||
@@ -405,9 +405,11 @@ private module Cached {
|
||||
* `fresh` indicates whether the instance `this` in `c` has been freshly
|
||||
* allocated along the call-chain.
|
||||
*/
|
||||
overlay[global]
|
||||
private newtype TCallableNode =
|
||||
MkCallableNode(Callable c, boolean fresh) { source(_, _, _, c, fresh) or edge(_, c, fresh) }
|
||||
|
||||
overlay[global]
|
||||
private predicate edge(TCallableNode n, Callable c2, boolean f2) {
|
||||
exists(Callable c1, boolean f1 | n = MkCallableNode(c1, f1) |
|
||||
intraInstanceCallEdge(c1, c2) and f2 = f1
|
||||
@@ -417,6 +419,7 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private predicate edge(TCallableNode n1, TCallableNode n2) {
|
||||
exists(Callable c2, boolean f2 |
|
||||
edge(n1, c2, f2) and
|
||||
@@ -424,6 +427,7 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
pragma[noinline]
|
||||
private predicate source(Call call, TrackedField f, Field field, TCallableNode n) {
|
||||
exists(Callable c, boolean fresh |
|
||||
@@ -432,24 +436,28 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private predicate sink(Callable c, Field f, TCallableNode n) {
|
||||
setsOwnField(c, f) and n = MkCallableNode(c, false)
|
||||
or
|
||||
setsOtherField(c, f) and n = MkCallableNode(c, _)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private predicate prunedNode(TCallableNode n) {
|
||||
sink(_, _, n)
|
||||
or
|
||||
exists(TCallableNode mid | edge(n, mid) and prunedNode(mid))
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private predicate prunedEdge(TCallableNode n1, TCallableNode n2) {
|
||||
prunedNode(n1) and
|
||||
prunedNode(n2) and
|
||||
edge(n1, n2)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
private predicate edgePlus(TCallableNode c1, TCallableNode c2) = fastTC(prunedEdge/2)(c1, c2)
|
||||
|
||||
/**
|
||||
@@ -457,6 +465,7 @@ private module Cached {
|
||||
* where `f` and `call` share the same enclosing callable in which a
|
||||
* `FieldRead` of `f` is reachable from `call`.
|
||||
*/
|
||||
overlay[global]
|
||||
pragma[noopt]
|
||||
private predicate updatesNamedFieldImpl(Call call, TrackedField f, Callable setter) {
|
||||
exists(TCallableNode src, TCallableNode sink, Field field |
|
||||
@@ -467,11 +476,13 @@ private module Cached {
|
||||
}
|
||||
|
||||
bindingset[call, f]
|
||||
overlay[global]
|
||||
pragma[inline_late]
|
||||
private predicate updatesNamedField0(Call call, TrackedField f, Callable setter) {
|
||||
updatesNamedField(call, f, setter)
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
cached
|
||||
predicate defUpdatesNamedField(SsaImplicitUpdate def, TrackedField f, Callable setter) {
|
||||
f = def.getSourceVariable() and
|
||||
|
||||
@@ -278,21 +278,23 @@ private predicate inputStreamWrapper(Constructor c, int argi) {
|
||||
|
||||
/** An object construction that preserves the data flow status of any of its arguments. */
|
||||
private predicate constructorStep(Expr tracked, ConstructorCall sink, string model) {
|
||||
exists(int argi | sink.getArgument(argi) = tracked |
|
||||
exists(int argi | sink.getArgument(pragma[only_bind_into](argi)) = tracked |
|
||||
// wrappers constructed by extension
|
||||
exists(Constructor c, Parameter p, SuperConstructorInvocationStmt sup |
|
||||
c = sink.getConstructor() and
|
||||
p = c.getParameter(argi) and
|
||||
p = c.getParameter(pragma[only_bind_into](argi)) and
|
||||
sup.getEnclosingCallable() = c and
|
||||
constructorStep(p.getAnAccess(), sup, model)
|
||||
)
|
||||
or
|
||||
// a custom InputStream that wraps a tainted data source is tainted
|
||||
model = "inputStreamWrapper" and
|
||||
inputStreamWrapper(sink.getConstructor(), argi)
|
||||
inputStreamWrapper(sink.getConstructor(), pragma[only_bind_into](argi))
|
||||
or
|
||||
model = "TaintPreservingCallable" and
|
||||
sink.getConstructor().(TaintPreservingCallable).returnsTaintFrom(argToParam(sink, argi))
|
||||
sink.getConstructor()
|
||||
.(TaintPreservingCallable)
|
||||
.returnsTaintFrom(argToParam(sink, pragma[only_bind_into](argi)))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ private module Impl {
|
||||
/** Gets the character value of expression `e`. */
|
||||
string getCharValue(Expr e) { result = e.(CharacterLiteral).getValue() }
|
||||
|
||||
/** Gets the constant `float` value of non-`ConstantIntegerExpr` expressions. */
|
||||
/** Gets the constant `float` value of non-`ConstantIntegerExpr` expression `e`. */
|
||||
float getNonIntegerValue(Expr e) {
|
||||
result = e.(LongLiteral).getValue().toFloat() or
|
||||
result = e.(FloatLiteral).getValue().toFloat() or
|
||||
@@ -256,12 +256,12 @@ private module Impl {
|
||||
exists(EnhancedForStmt for | def = for.getVariable())
|
||||
}
|
||||
|
||||
/** Returns the operand of the operation if `def` is a decrement. */
|
||||
/** Returns the operand of the operation if `e` is a decrement. */
|
||||
Expr getDecrementOperand(Element e) {
|
||||
result = e.(PostDecExpr).getExpr() or result = e.(PreDecExpr).getExpr()
|
||||
}
|
||||
|
||||
/** Returns the operand of the operation if `def` is an increment. */
|
||||
/** Returns the operand of the operation if `e` is an increment. */
|
||||
Expr getIncrementOperand(Element e) {
|
||||
result = e.(PostIncExpr).getExpr() or result = e.(PreIncExpr).getExpr()
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
* data flow check for lambdas, anonymous classes, and other sufficiently
|
||||
* private classes where all object instantiations are accounted for.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
private import VirtualDispatch
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
* The set of dispatch targets for `Object.toString()` calls are reduced based
|
||||
* on possible data flow from objects of more specific types to the qualifier.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
private import VirtualDispatch
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
* Provides predicates for reasoning about runtime call targets through virtual
|
||||
* dispatch.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TypeFlow
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
/**
|
||||
* Provides a module to check whether two `ParameterizedType`s are unifiable.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/**
|
||||
* DEPRECATED.
|
||||
*
|
||||
* A library providing uniform access to various assertion frameworks.
|
||||
*
|
||||
* Currently supports `org.junit.Assert`, `junit.framework.*`,
|
||||
@@ -6,7 +8,7 @@
|
||||
* and `java.util.Objects`.
|
||||
*/
|
||||
overlay[local?]
|
||||
module;
|
||||
deprecated module;
|
||||
|
||||
import java
|
||||
|
||||
|
||||
@@ -223,10 +223,10 @@ class MockitoInjectedField extends MockitoAnnotatedField {
|
||||
// If there is no initializer for this field, and there is a most mockable constructor,
|
||||
// then we are doing a parameterized injection of mocks into a most mockable constructor.
|
||||
result = mockInjectedClass.getAMostMockableConstructor()
|
||||
else
|
||||
if this.usingPropertyInjection()
|
||||
then
|
||||
// We will call the no-arg constructor if the field wasn't initialized.
|
||||
else (
|
||||
this.usingPropertyInjection() and
|
||||
// We will call the no-arg constructor if the field wasn't initialized.
|
||||
(
|
||||
not exists(this.getInitializer()) and
|
||||
result = mockInjectedClass.getNoArgsConstructor()
|
||||
or
|
||||
@@ -241,9 +241,8 @@ class MockitoInjectedField extends MockitoAnnotatedField {
|
||||
// once, but we instead assume that there are sufficient mocks to go around.
|
||||
mockedField.getType().(RefType).getAnAncestor() = result.getParameterType(0)
|
||||
)
|
||||
else
|
||||
// There's no instance, and no no-arg constructor we can call, so injection fails.
|
||||
none()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -253,18 +252,16 @@ class MockitoInjectedField extends MockitoAnnotatedField {
|
||||
* Field injection only occurs if property injection and not constructor injection is used.
|
||||
*/
|
||||
Field getASetField() {
|
||||
if this.usingPropertyInjection()
|
||||
then
|
||||
result = this.getMockInjectedClass().getASetField() and
|
||||
exists(MockitoMockedField mockedField |
|
||||
mockedField.getDeclaringType() = this.getDeclaringType() and
|
||||
mockedField.isValid()
|
||||
|
|
||||
// We make a simplifying assumption here - in theory, each mock can only be injected
|
||||
// once, but we instead assume that there are sufficient mocks to go around.
|
||||
mockedField.getType().(RefType).getAnAncestor() = result.getType()
|
||||
)
|
||||
else none()
|
||||
this.usingPropertyInjection() and
|
||||
result = this.getMockInjectedClass().getASetField() and
|
||||
exists(MockitoMockedField mockedField |
|
||||
mockedField.getDeclaringType() = this.getDeclaringType() and
|
||||
mockedField.isValid()
|
||||
|
|
||||
// We make a simplifying assumption here - in theory, each mock can only be injected
|
||||
// once, but we instead assume that there are sufficient mocks to go around.
|
||||
mockedField.getType().(RefType).getAnAncestor() = result.getType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,10 @@ private predicate externalStorageFlowStep(DataFlow::Node node1, DataFlow::Node n
|
||||
node2.asExpr().(FieldRead).getField().getInitializer() = node1.asExpr()
|
||||
}
|
||||
|
||||
private predicate externalStorageFlow(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
externalStorageFlowStep*(node1, node2)
|
||||
private predicate externalStorageDirFlowsTo(DataFlow::Node n) {
|
||||
sourceNode(n, "android-external-storage-dir")
|
||||
or
|
||||
exists(DataFlow::Node mid | externalStorageDirFlowsTo(mid) and externalStorageFlowStep(mid, n))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,9 +31,8 @@ private predicate externalStorageFlow(DataFlow::Node node1, DataFlow::Node node2
|
||||
* This is controllable by third-party applications, so is treated as a remote flow source.
|
||||
*/
|
||||
predicate androidExternalStorageSource(DataFlow::Node n) {
|
||||
exists(DataFlow::Node externalDir, DirectFileReadExpr read |
|
||||
sourceNode(externalDir, "android-external-storage-dir") and
|
||||
exists(DirectFileReadExpr read |
|
||||
n.asExpr() = read and
|
||||
externalStorageFlow(externalDir, DataFlow::exprNode(read.getFileExpr()))
|
||||
externalStorageDirFlowsTo(DataFlow::exprNode(read.getFileExpr()))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,14 +35,14 @@ class SessionEjb extends EJB {
|
||||
// Either the EJB does not declare any business interfaces explicitly
|
||||
// and implements a single interface candidate,
|
||||
// which is then considered to be the business interface...
|
||||
count(this.getAnExplicitBusinessInterface()) = 0 and
|
||||
not exists(this.getAnExplicitBusinessInterface()) and
|
||||
count(this.getAnImplementedBusinessInterfaceCandidate()) = 1 and
|
||||
result = this.getAnImplementedBusinessInterfaceCandidate()
|
||||
or
|
||||
// ...or each business interface needs to be declared explicitly.
|
||||
(
|
||||
count(this.getAnImplementedBusinessInterfaceCandidate()) != 1 or
|
||||
count(this.getAnExplicitBusinessInterface()) != 0
|
||||
exists(this.getAnExplicitBusinessInterface())
|
||||
) and
|
||||
result = this.getAnExplicitBusinessInterface()
|
||||
}
|
||||
|
||||
@@ -163,6 +163,12 @@ private module RegexFlowConfig implements DataFlow::ConfigSig {
|
||||
|
||||
private module RegexFlow = DataFlow::Global<RegexFlowConfig>;
|
||||
|
||||
private predicate usedAsRegexImpl(StringLiteral regex, string mode, boolean match_full_string) {
|
||||
RegexFlow::flow(DataFlow::exprNode(regex), _) and
|
||||
mode = "None" and // TODO: proper mode detection
|
||||
(if matchesFullString(regex) then match_full_string = true else match_full_string = false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `regex` is used as a regex, with the mode `mode` (if known).
|
||||
* If regex mode is not known, `mode` will be `"None"`.
|
||||
@@ -170,11 +176,9 @@ private module RegexFlow = DataFlow::Global<RegexFlowConfig>;
|
||||
* As an optimisation, only regexes containing an infinite repitition quatifier (`+`, `*`, or `{x,}`)
|
||||
* and therefore may be relevant for ReDoS queries are considered.
|
||||
*/
|
||||
predicate usedAsRegex(StringLiteral regex, string mode, boolean match_full_string) {
|
||||
RegexFlow::flow(DataFlow::exprNode(regex), _) and
|
||||
mode = "None" and // TODO: proper mode detection
|
||||
(if matchesFullString(regex) then match_full_string = true else match_full_string = false)
|
||||
}
|
||||
overlay[local]
|
||||
predicate usedAsRegex(StringLiteral regex, string mode, boolean match_full_string) =
|
||||
forceLocal(usedAsRegexImpl/3)(regex, mode, match_full_string)
|
||||
|
||||
/**
|
||||
* Holds if `regex` is used as a regular expression that is matched against a full string,
|
||||
|
||||
@@ -55,7 +55,7 @@ private predicate isVarargs(Argument arg, DataFlow::ImplicitVarargsArray varargs
|
||||
arg.isVararg() and arg.getCall() = varargs.getCall()
|
||||
}
|
||||
|
||||
/** Holds if `store` closes `file`. */
|
||||
/** Holds if `closeCall` closes `file`. */
|
||||
private predicate closesFile(DataFlow::Node file, Call closeCall) {
|
||||
closeCall.getCallee() instanceof CloseFileMethod and
|
||||
if closeCall.getCallee().isStatic()
|
||||
|
||||
@@ -89,7 +89,7 @@ private VarAccess getFileForPathConversion(Expr pathExpr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fileAccess` is used in the `setWorldWritableExpr` to set the file to be world writable.
|
||||
* Holds if `fileAccess` is used in the `setWorldWritable` to set the file to be world writable.
|
||||
*/
|
||||
private predicate fileSetWorldWritable(VarAccess fileAccess, Expr setWorldWritable) {
|
||||
// Calls to `File.setWritable(.., false)`.
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/** Provides classes and predicates to reason about Spring Boot actuators exposed in configuration files. */
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
private import semmle.code.configfiles.ConfigFiles
|
||||
private import semmle.code.xml.MavenPom
|
||||
|
||||
/** The parent node of the `org.springframework.boot` group. */
|
||||
private class SpringBootParent extends Parent {
|
||||
SpringBootParent() { this.getGroup().getValue() = "org.springframework.boot" }
|
||||
}
|
||||
|
||||
/** A `Pom` with a Spring Boot parent node. */
|
||||
private class SpringBootPom extends Pom {
|
||||
SpringBootPom() { this.getParentElement() instanceof SpringBootParent }
|
||||
|
||||
/** Holds if the Spring Boot Security module is used in the project. */
|
||||
predicate isSpringBootSecurityUsed() {
|
||||
this.getADependency().getArtifact().getValue() = "spring-boot-starter-security"
|
||||
}
|
||||
}
|
||||
|
||||
/** A dependency with artifactId `spring-boot-starter-actuator`. */
|
||||
class SpringBootStarterActuatorDependency extends Dependency {
|
||||
SpringBootStarterActuatorDependency() {
|
||||
this.getArtifact().getValue() = "spring-boot-starter-actuator"
|
||||
}
|
||||
}
|
||||
|
||||
/** The Spring Boot configuration property `management.security.enabled`. */
|
||||
private class ManagementSecurityEnabledProperty extends JavaProperty {
|
||||
ManagementSecurityEnabledProperty() {
|
||||
this.getNameElement().getName() = "management.security.enabled"
|
||||
}
|
||||
|
||||
/** Gets the whitespace-trimmed value of this property. */
|
||||
string getValue() { result = this.getValueElement().getValue().trim() }
|
||||
|
||||
/** Holds if `management.security.enabled` is set to `false`. */
|
||||
predicate hasSecurityDisabled() { this.getValue() = "false" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The Spring Boot configuration property `management.endpoints.web.exposure.include`
|
||||
* or `management.endpoints.web.expose`.
|
||||
*/
|
||||
private class ManagementEndpointsExposeProperty extends JavaProperty {
|
||||
ManagementEndpointsExposeProperty() {
|
||||
this.getNameElement().getName() = "management.endpoints.web." + ["exposure.include", "expose"]
|
||||
}
|
||||
|
||||
/** Gets the whitespace-trimmed value of this property. */
|
||||
string getValue() { result = this.getValueElement().getValue().trim() }
|
||||
}
|
||||
|
||||
private newtype TOption =
|
||||
TNone() or
|
||||
TSome(JavaProperty jp)
|
||||
|
||||
/**
|
||||
* An option type that is either a singleton `None` or a `Some` wrapping
|
||||
* the `JavaProperty` type.
|
||||
*/
|
||||
class JavaPropertyOption extends TOption {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
this = TNone() and result = "(none)"
|
||||
or
|
||||
result = this.asSome().toString()
|
||||
}
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { result = this.asSome().getLocation() }
|
||||
|
||||
/** Gets the wrapped element, if any. */
|
||||
JavaProperty asSome() { this = TSome(result) }
|
||||
|
||||
/** Holds if this option is the singleton `None`. */
|
||||
predicate isNone() { this = TNone() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `JavaPropertyOption` jpOption of a repository using `SpringBootStarterActuatorDependency`
|
||||
* d exposes sensitive Spring Boot Actuator endpoints.
|
||||
*/
|
||||
predicate exposesSensitiveEndpoint(
|
||||
SpringBootStarterActuatorDependency d, JavaPropertyOption jpOption
|
||||
) {
|
||||
exists(PropertiesFile propFile, SpringBootPom pom |
|
||||
d = pom.getADependency() and
|
||||
not pom.isSpringBootSecurityUsed() and
|
||||
propFile
|
||||
.getParentContainer()
|
||||
.getAbsolutePath()
|
||||
.matches(pom.getFile().getParentContainer().getAbsolutePath() + "%") and // in the same sub-directory
|
||||
exists(string springBootVersion |
|
||||
springBootVersion = pom.getParentElement().getVersionString()
|
||||
|
|
||||
springBootVersion.regexpMatch("1\\.[0-4].*") and // version 1.0, 1.1, ..., 1.4
|
||||
not exists(ManagementSecurityEnabledProperty ep | ep.getFile() = propFile) and
|
||||
jpOption.isNone()
|
||||
or
|
||||
springBootVersion.regexpMatch("1\\.[0-5].*") and // version 1.0, 1.1, ..., 1.5
|
||||
exists(ManagementSecurityEnabledProperty ep |
|
||||
ep.hasSecurityDisabled() and ep.getFile() = propFile and ep = jpOption.asSome()
|
||||
)
|
||||
or
|
||||
springBootVersion.matches(["2.%", "3.%"]) and //version 2.x and 3.x
|
||||
exists(ManagementEndpointsExposeProperty ep |
|
||||
ep.getFile() = propFile and
|
||||
ep = jpOption.asSome() and
|
||||
(
|
||||
// all endpoints are exposed
|
||||
ep.getValue() = "*"
|
||||
or
|
||||
// version 2.x: exposes health and info only by default
|
||||
springBootVersion.matches("2.%") and
|
||||
not ep.getValue() = ["health", "info"]
|
||||
or
|
||||
// version 3.x: exposes health only by default
|
||||
springBootVersion.matches("3.%") and
|
||||
not ep.getValue() = "health"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -26,7 +26,8 @@ class MethodFileCreateTempFile extends Method {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `expDest` is some constructor call `new java.io.File(expSource)`, where the specific `File` constructor being used has `paramCount` parameters.
|
||||
* Holds if `expSource` is an argument to a constructor call `exprDest` (constructor from `java.io.File`), where
|
||||
* the specific `File` constructor being used has `paramCount` parameters.
|
||||
*/
|
||||
predicate isFileConstructorArgument(Expr expSource, Expr exprDest, int paramCount) {
|
||||
exists(ConstructorCall construtorCall |
|
||||
|
||||
@@ -552,7 +552,7 @@ private DataFlow::Node getASafelyConfiguredParser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `parseMethodQualifierExpr` is a `jodd.json.JsonParser` instance that is configured unsafely
|
||||
* Holds if `parserExpr` is a `jodd.json.JsonParser` instance that is configured unsafely
|
||||
* and which never appears to be configured safely.
|
||||
*/
|
||||
private predicate joddJsonParserConfiguredUnsafely(Expr parserExpr) {
|
||||
|
||||
@@ -6,6 +6,7 @@ module;
|
||||
|
||||
import semmle.files.FileSystem
|
||||
private import codeql.xml.Xml
|
||||
private import semmle.code.java.Overlay
|
||||
|
||||
private module Input implements InputSig<File, Location> {
|
||||
class XmlLocatableBase = @xmllocatable or @xmlnamespaceable;
|
||||
@@ -69,3 +70,13 @@ private module Input implements InputSig<File, Location> {
|
||||
}
|
||||
|
||||
import Make<File, Location, Input>
|
||||
|
||||
private class DiscardableXmlAttribute extends DiscardableXmlLocatable, @xmlattribute { }
|
||||
|
||||
private class DiscardableXmlElement extends DiscardableXmlLocatable, @xmlelement { }
|
||||
|
||||
private class DiscardableXmlComment extends DiscardableXmlLocatable, @xmlcomment { }
|
||||
|
||||
private class DiscardableXmlCharacters extends DiscardableXmlLocatable, @xmlcharacters { }
|
||||
|
||||
private class DiscardableXmlDtd extends DiscardableXmlLocatable, @xmldtd { }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Add support for Java 25 compact source files by introducing isImplicitClass table
|
||||
compatibility: full
|
||||
@@ -7,7 +7,10 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/non-final-immutable-field
|
||||
* @tags reliability
|
||||
* @tags quality
|
||||
* reliability
|
||||
* correctness
|
||||
* readability
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/non-private-field
|
||||
* @tags maintainability
|
||||
* @tags quality
|
||||
* maintainability
|
||||
* readability
|
||||
* complexity
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* @precision medium
|
||||
* @id java/undocumented-function
|
||||
* @tags maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* @precision medium
|
||||
* @id java/undocumented-parameter
|
||||
* @tags maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* @precision medium
|
||||
* @id java/undocumented-return-value
|
||||
* @tags maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
* @precision medium
|
||||
* @id java/undocumented-exception
|
||||
* @tags maintainability
|
||||
* readability
|
||||
* error-handling
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
* @precision medium
|
||||
* @id java/undocumented-type
|
||||
* @tags maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/use-of-clone-method
|
||||
* @tags reliability
|
||||
* @tags quality
|
||||
* reliability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/override-of-clone-method
|
||||
* @tags reliability
|
||||
* @tags quality
|
||||
* reliability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/use-of-cloneable-interface
|
||||
* @tags reliability
|
||||
* @tags quality
|
||||
* reliability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/override-of-finalize-method
|
||||
* @tags reliability
|
||||
* @tags quality
|
||||
* reliability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/misnamed-constant
|
||||
* @tags maintainability
|
||||
* @tags quality
|
||||
* maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/misnamed-function
|
||||
* @tags maintainability
|
||||
* @tags quality
|
||||
* maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/misnamed-package
|
||||
* @tags maintainability
|
||||
* @tags quality
|
||||
* maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/misnamed-type
|
||||
* @tags maintainability
|
||||
* @tags quality
|
||||
* maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/misnamed-variable
|
||||
* @tags maintainability
|
||||
* @tags quality
|
||||
* maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/missing-default-in-switch
|
||||
* @tags reliability
|
||||
* @tags quality
|
||||
* reliability
|
||||
* correctness
|
||||
* external/cwe/cwe-478
|
||||
*/
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/multiple-statements-on-same-line
|
||||
* @tags maintainability
|
||||
* @tags quality
|
||||
* maintainability
|
||||
* readability
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/non-terminated-if-else-if-chain
|
||||
* @tags reliability
|
||||
* @tags quality
|
||||
* reliability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/raw-constructor-invocation
|
||||
* @tags maintainability
|
||||
* @tags quality
|
||||
* maintainability
|
||||
* readability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/raw-return-type
|
||||
* @tags maintainability
|
||||
* @tags quality
|
||||
* maintainability
|
||||
* readability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @id java/raw-variable
|
||||
* @tags maintainability
|
||||
* @tags quality
|
||||
* maintainability
|
||||
* readability
|
||||
* correctness
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user