mirror of
https://github.com/github/codeql.git
synced 2026-07-03 10:35:29 +02:00
Compare commits
77 Commits
codeql-cli
...
andersfugm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b82ec0e0f9 | ||
|
|
bc2d7108f1 | ||
|
|
ccfd99a2be | ||
|
|
73e69c6466 | ||
|
|
0196150c51 | ||
|
|
a3dbdcd01a | ||
|
|
f1cc1e5c47 | ||
|
|
041a8e6adc | ||
|
|
fb424020af | ||
|
|
bda8e7dae1 | ||
|
|
37c8111c18 | ||
|
|
807bb51df7 | ||
|
|
b6abfe6e5c | ||
|
|
b3dc7009a4 | ||
|
|
e59f646870 | ||
|
|
cc3c232631 | ||
|
|
9a5cc3c5e3 | ||
|
|
3983e4db29 | ||
|
|
3058198c0d | ||
|
|
2ef06c9f96 | ||
|
|
1842382e23 | ||
|
|
db449dca6a | ||
|
|
7216d12b9a | ||
|
|
c4b4fde0d7 | ||
|
|
46382cbc8e | ||
|
|
da3d0cf977 | ||
|
|
93439db87b | ||
|
|
70ca7af04c | ||
|
|
664f0125b9 | ||
|
|
1b7f589000 | ||
|
|
eb7f8cc43d | ||
|
|
2767b8dbbf | ||
|
|
b1f60acf2c | ||
|
|
2b2613de4e | ||
|
|
14acc7fcab | ||
|
|
37ce885b0c | ||
|
|
52acaec03d | ||
|
|
d6e8555f8b | ||
|
|
b5ef15c70f | ||
|
|
5735ac330d | ||
|
|
5348c7d07c | ||
|
|
f89f304e50 | ||
|
|
ff7dc297d5 | ||
|
|
cacdc467de | ||
|
|
7b800b1dd6 | ||
|
|
1b6ff24642 | ||
|
|
3d1b6b64ed | ||
|
|
ac618e1cb2 | ||
|
|
221a54d22e | ||
|
|
5fcaac7cb2 | ||
|
|
cc215858e4 | ||
|
|
56a1b12c9e | ||
|
|
688213056c | ||
|
|
1c37688ec1 | ||
|
|
336df3ccf4 | ||
|
|
587f9c24ed | ||
|
|
456e33773b | ||
|
|
af7ae8c4cb | ||
|
|
1c4552edb0 | ||
|
|
5136d872ae | ||
|
|
474bcd4dd1 | ||
|
|
199489a225 | ||
|
|
ae4ccc651c | ||
|
|
0d845c2ea9 | ||
|
|
6d138c2bd4 | ||
|
|
85c39c04e0 | ||
|
|
1ee142d8bd | ||
|
|
a523c7f47f | ||
|
|
5f73754b95 | ||
|
|
e0fa6cf785 | ||
|
|
d1d9df7729 | ||
|
|
9bffcf81b5 | ||
|
|
f7c4e61956 | ||
|
|
575ece6ae2 | ||
|
|
f6ed5c19be | ||
|
|
4298b70f1c | ||
|
|
e88b8c53f3 |
@@ -28,7 +28,6 @@
|
||||
/swift/extractor/ @github/codeql-swift @github/code-scanning-language-coverage
|
||||
/misc/codegen/ @github/codeql-swift
|
||||
/java/kotlin-extractor/ @github/codeql-kotlin @github/code-scanning-language-coverage
|
||||
/java/ql/test-kotlin1/ @github/codeql-kotlin
|
||||
/java/ql/test-kotlin2/ @github/codeql-kotlin
|
||||
|
||||
# Experimental CodeQL cryptography
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/actions-all
|
||||
version: 0.4.38
|
||||
version: 0.4.39-dev
|
||||
library: true
|
||||
warnOnImplicitThis: true
|
||||
dependencies:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/actions-queries
|
||||
version: 0.6.30
|
||||
version: 0.6.31-dev
|
||||
library: false
|
||||
warnOnImplicitThis: true
|
||||
groups: [actions, queries]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 11.0.0
|
||||
version: 11.0.1-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 1.6.5
|
||||
version: 1.6.6-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.7.69
|
||||
version: 1.7.70-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.7.69
|
||||
version: 1.7.70-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 7.0.0
|
||||
version: 7.0.1-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 1.7.5
|
||||
version: 1.7.6-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
@@ -10,7 +10,7 @@ toolchain go1.26.4
|
||||
// bazel mod tidy
|
||||
require (
|
||||
golang.org/x/mod v0.37.0
|
||||
golang.org/x/tools v0.46.0
|
||||
golang.org/x/tools v0.47.0
|
||||
)
|
||||
|
||||
require github.com/stretchr/testify v1.11.1
|
||||
|
||||
@@ -10,8 +10,8 @@ golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk=
|
||||
golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys=
|
||||
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
|
||||
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql-go-consistency-queries
|
||||
version: 1.0.52
|
||||
version: 1.0.53-dev
|
||||
groups:
|
||||
- go
|
||||
- queries
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/go-all
|
||||
version: 7.2.0
|
||||
version: 7.2.1-dev
|
||||
groups: go
|
||||
dbscheme: go.dbscheme
|
||||
extractor: go
|
||||
|
||||
@@ -33,9 +33,11 @@ module StoredXss {
|
||||
walkFn.getACall().getArgument(1) = f.getASuccessor*()
|
||||
)
|
||||
or
|
||||
// A call to os.FileInfo.Name
|
||||
exists(Method m | m.implements("io/fs", "FileInfo", "Name") |
|
||||
m = this.(DataFlow::CallNode).getTarget()
|
||||
// The return value of a call to `os.DirEntry.Name`, `os.FileInfo.Name`
|
||||
// or `os.File.ReadDirNames`.
|
||||
exists(DataFlow::CallNode cn, Method m | m = cn.getTarget() and this = cn.getResult(0) |
|
||||
m.implements("io/fs", ["DirEntry", "FileInfo"], "Name") or
|
||||
m.hasQualifiedName("os", "File", "ReadDirNames")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/go-queries
|
||||
version: 1.6.5
|
||||
version: 1.6.6-dev
|
||||
groups:
|
||||
- go
|
||||
- queries
|
||||
|
||||
@@ -156,12 +156,3 @@ nodes
|
||||
| websocketXss.go:54:3:54:38 | ... := ...[1] | semmle.label | ... := ...[1] |
|
||||
| websocketXss.go:55:24:55:31 | gorilla3 | semmle.label | gorilla3 |
|
||||
subpaths
|
||||
testFailures
|
||||
| websocketXss.go:30:32:30:60 | comment | Missing result: Source[go/reflected-xss] |
|
||||
| websocketXss.go:31:11:31:14 | xnet [postupdate] | Unexpected result: Source |
|
||||
| websocketXss.go:34:30:34:58 | comment | Missing result: Source[go/reflected-xss] |
|
||||
| websocketXss.go:35:21:35:25 | xnet2 [postupdate] | Unexpected result: Source |
|
||||
| websocketXss.go:46:38:46:66 | comment | Missing result: Source[go/reflected-xss] |
|
||||
| websocketXss.go:47:26:47:35 | gorillaMsg [postupdate] | Unexpected result: Source |
|
||||
| websocketXss.go:50:33:50:61 | comment | Missing result: Source[go/reflected-xss] |
|
||||
| websocketXss.go:51:17:51:24 | gorilla2 [postupdate] | Unexpected result: Source |
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#select
|
||||
| StoredXss.go:13:21:13:36 | ...+... | StoredXss.go:13:21:13:31 | call to Name | StoredXss.go:13:21:13:36 | ...+... | Stored cross-site scripting vulnerability due to $@. | StoredXss.go:13:21:13:31 | call to Name | stored value |
|
||||
| stored.go:30:22:30:25 | name | stored.go:18:3:18:28 | ... := ...[0] | stored.go:30:22:30:25 | name | Stored cross-site scripting vulnerability due to $@. | stored.go:18:3:18:28 | ... := ...[0] | stored value |
|
||||
| stored.go:61:22:61:25 | path | stored.go:59:30:59:33 | SSA def(path) | stored.go:61:22:61:25 | path | Stored cross-site scripting vulnerability due to $@. | stored.go:59:30:59:33 | SSA def(path) | stored value |
|
||||
edges
|
||||
| StoredXss.go:13:21:13:31 | call to Name | StoredXss.go:13:21:13:36 | ...+... | provenance | |
|
||||
| stored.go:18:3:18:28 | ... := ...[0] | stored.go:25:14:25:17 | rows | provenance | Src:MaD:1 |
|
||||
| stored.go:25:14:25:17 | rows | stored.go:25:29:25:33 | &... [postupdate] | provenance | FunctionModel |
|
||||
| stored.go:25:29:25:33 | &... [postupdate] | stored.go:30:22:30:25 | name | provenance | |
|
||||
@@ -9,6 +11,8 @@ edges
|
||||
models
|
||||
| 1 | Source: database/sql; DB; true; Query; ; ; ReturnValue[0]; database; manual |
|
||||
nodes
|
||||
| StoredXss.go:13:21:13:31 | call to Name | semmle.label | call to Name |
|
||||
| StoredXss.go:13:21:13:36 | ...+... | semmle.label | ...+... |
|
||||
| stored.go:18:3:18:28 | ... := ...[0] | semmle.label | ... := ...[0] |
|
||||
| stored.go:25:14:25:17 | rows | semmle.label | rows |
|
||||
| stored.go:25:29:25:33 | &... [postupdate] | semmle.label | &... [postupdate] |
|
||||
@@ -16,5 +20,3 @@ nodes
|
||||
| stored.go:59:30:59:33 | SSA def(path) | semmle.label | SSA def(path) |
|
||||
| stored.go:61:22:61:25 | path | semmle.label | path |
|
||||
subpaths
|
||||
testFailures
|
||||
| StoredXss.go:13:39:13:63 | comment | Missing result: Alert[go/stored-xss] |
|
||||
|
||||
@@ -27,12 +27,12 @@ func xss(w http.ResponseWriter, r *http.Request) {
|
||||
origin := "test"
|
||||
{
|
||||
ws, _ := websocket.Dial(uri, "", origin)
|
||||
var xnet = make([]byte, 512) // $ Source[go/reflected-xss]
|
||||
ws.Read(xnet)
|
||||
var xnet = make([]byte, 512)
|
||||
ws.Read(xnet) // $ Source[go/reflected-xss]
|
||||
fmt.Fprintf(w, "%v", xnet) // $ Alert[go/reflected-xss]
|
||||
codec := &websocket.Codec{Marshal: marshal, Unmarshal: unmarshal}
|
||||
xnet2 := make([]byte, 512) // $ Source[go/reflected-xss]
|
||||
codec.Receive(ws, xnet2)
|
||||
xnet2 := make([]byte, 512)
|
||||
codec.Receive(ws, xnet2) // $ Source[go/reflected-xss]
|
||||
fmt.Fprintf(w, "%v", xnet2) // $ Alert[go/reflected-xss]
|
||||
}
|
||||
{
|
||||
@@ -43,12 +43,12 @@ func xss(w http.ResponseWriter, r *http.Request) {
|
||||
{
|
||||
dialer := gorilla.Dialer{}
|
||||
conn, _, _ := dialer.Dial(uri, nil)
|
||||
var gorillaMsg = make([]byte, 512) // $ Source[go/reflected-xss]
|
||||
gorilla.ReadJSON(conn, gorillaMsg)
|
||||
fmt.Fprintf(w, "%v", gorillaMsg) // $ Alert[go/reflected-xss]
|
||||
var gorillaMsg = make([]byte, 512)
|
||||
gorilla.ReadJSON(conn, gorillaMsg) // $ Source[go/reflected-xss]
|
||||
fmt.Fprintf(w, "%v", gorillaMsg) // $ Alert[go/reflected-xss]
|
||||
|
||||
gorilla2 := make([]byte, 512) // $ Source[go/reflected-xss]
|
||||
conn.ReadJSON(gorilla2)
|
||||
gorilla2 := make([]byte, 512)
|
||||
conn.ReadJSON(gorilla2) // $ Source[go/reflected-xss]
|
||||
fmt.Fprintf(w, "%v", gorilla2) // $ Alert[go/reflected-xss]
|
||||
|
||||
_, gorilla3, _ := conn.ReadMessage() // $ Source[go/reflected-xss]
|
||||
|
||||
@@ -6,6 +6,8 @@ import com.github.codeql.utils.*
|
||||
import com.github.codeql.utils.versions.*
|
||||
import com.semmle.extractor.java.OdasaOutput
|
||||
import java.io.Closeable
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
@@ -50,6 +52,7 @@ import org.jetbrains.kotlin.load.java.structure.JavaMethod
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameterListOwner
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
|
||||
import org.jetbrains.kotlin.fir.java.VirtualFileBasedSourceElement
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
@@ -161,11 +164,60 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun javaBinaryDeclaresMethod(c: IrClass, name: String) =
|
||||
((c.source as? JavaSourceElement)?.javaElement as? BinaryJavaClass)?.methods?.any {
|
||||
it.name.asString() == name
|
||||
private fun javaBinaryDeclaresMethod(c: IrClass, name: String): Boolean? {
|
||||
// K1 path: source is JavaSourceElement wrapping a BinaryJavaClass - inspect class metadata
|
||||
val binaryJavaClass = (c.source as? JavaSourceElement)?.javaElement as? BinaryJavaClass
|
||||
if (binaryJavaClass != null) {
|
||||
return binaryJavaClass.methods.any { it.name.asString() == name }
|
||||
}
|
||||
|
||||
// K2 path: binary Java classes use VirtualFileBasedSourceElement instead of
|
||||
// JavaSourceElement. The BinaryJavaClass is not stored in the source element, so we parse
|
||||
// the class bytes directly using ASM to check if the method is explicitly declared.
|
||||
val virtualFile = (c.source as? VirtualFileBasedSourceElement)?.virtualFile
|
||||
if (virtualFile != null) {
|
||||
if (!virtualFile.name.endsWith(".class")) return null
|
||||
return try {
|
||||
val bytes = virtualFile.contentsToByteArray()
|
||||
var found = false
|
||||
var hasKotlinMetadata = false
|
||||
val reader = org.jetbrains.org.objectweb.asm.ClassReader(bytes)
|
||||
reader.accept(
|
||||
object : org.jetbrains.org.objectweb.asm.ClassVisitor(
|
||||
org.jetbrains.org.objectweb.asm.Opcodes.ASM9
|
||||
) {
|
||||
override fun visitAnnotation(
|
||||
descriptor: String,
|
||||
visible: Boolean
|
||||
): org.jetbrains.org.objectweb.asm.AnnotationVisitor? {
|
||||
if (descriptor == "Lkotlin/Metadata;") hasKotlinMetadata = true
|
||||
return null
|
||||
}
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
methodName: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
exceptions: Array<String>?
|
||||
): org.jetbrains.org.objectweb.asm.MethodVisitor? {
|
||||
if (methodName == name) found = true
|
||||
return null
|
||||
}
|
||||
},
|
||||
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_CODE or
|
||||
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_DEBUG or
|
||||
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_FRAMES
|
||||
)
|
||||
if (hasKotlinMetadata) false else found
|
||||
} catch (e: Exception) {
|
||||
logger.warn("Failed to check binary class methods for ${c.fqNameWhenAvailable}: $e")
|
||||
null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun isJavaBinaryDeclaration(f: IrFunction) =
|
||||
f.parentClassOrNull?.let { javaBinaryDeclaresMethod(it, f.name.asString()) } ?: false
|
||||
|
||||
@@ -175,7 +227,14 @@ open class KotlinFileExtractor(
|
||||
when (d.name.asString()) {
|
||||
"toString" -> d.codeQlValueParameters.isEmpty()
|
||||
"hashCode" -> d.codeQlValueParameters.isEmpty()
|
||||
"equals" -> d.codeQlValueParameters.singleOrNull()?.type?.isNullableAny() ?: false
|
||||
// Under K2 (language version 2.0+), the Object.equals(Object) parameter is
|
||||
// typed as Any (non-nullable) rather than Any? (nullable). Under K1 it is Any?.
|
||||
// Accept both so the redeclaration is recovered consistently across compilers.
|
||||
"equals" ->
|
||||
d.codeQlValueParameters
|
||||
.singleOrNull()
|
||||
?.type
|
||||
?.let { it.isNullableAny() || it.isAny() } ?: false
|
||||
else -> false
|
||||
} && isJavaBinaryDeclaration(d)
|
||||
else -> false
|
||||
@@ -1312,27 +1371,28 @@ open class KotlinFileExtractor(
|
||||
): TypeResults {
|
||||
with("value parameter", vp) {
|
||||
val location = locOverride ?: getLocation(vp, classTypeArgsIncludingOuterClasses)
|
||||
val parentFunction = vp.parent as? IrFunction
|
||||
val javaCallable = parentFunction?.let { getJavaCallable(it) }
|
||||
val maybeAlteredType =
|
||||
(vp.parent as? IrFunction)?.let {
|
||||
parentFunction?.let {
|
||||
if (overridesCollectionsMethodWithAlteredParameterTypes(it))
|
||||
eraseCollectionsMethodParameterType(vp.type, it.name.asString(), idx)
|
||||
else if (
|
||||
(vp.parent as? IrConstructor)?.parentClassOrNull?.kind ==
|
||||
(parentFunction as? IrConstructor)?.parentClassOrNull?.kind ==
|
||||
ClassKind.ANNOTATION_CLASS
|
||||
)
|
||||
kClassToJavaClass(vp.type)
|
||||
else null
|
||||
} ?: vp.type
|
||||
val javaType =
|
||||
(vp.parent as? IrFunction)?.let {
|
||||
getJavaCallable(it)?.let { jCallable ->
|
||||
getJavaValueParameterType(jCallable, idx)
|
||||
}
|
||||
}
|
||||
val javaType = javaCallable?.let { jCallable -> getJavaValueParameterType(jCallable, idx) }
|
||||
val addParameterWildcardsByDefault =
|
||||
!getInnermostWildcardSupppressionAnnotation(vp) &&
|
||||
!(javaCallable == null &&
|
||||
parentFunction?.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB)
|
||||
val typeWithWildcards =
|
||||
addJavaLoweringWildcards(
|
||||
maybeAlteredType,
|
||||
!getInnermostWildcardSupppressionAnnotation(vp),
|
||||
addParameterWildcardsByDefault,
|
||||
javaType
|
||||
)
|
||||
val substitutedType =
|
||||
@@ -1346,9 +1406,9 @@ open class KotlinFileExtractor(
|
||||
vp.origin == IrDeclarationOrigin.UNDERSCORE_PARAMETER ||
|
||||
((vp.parent as? IrFunction)?.let { hasSynthesizedParameterNames(it) } ?: true)
|
||||
val javaParameter =
|
||||
when (val callable = (vp.parent as? IrFunction)?.let { getJavaCallable(it) }) {
|
||||
is JavaConstructor -> callable.valueParameters.getOrNull(idx)
|
||||
is JavaMethod -> callable.valueParameters.getOrNull(idx)
|
||||
when (javaCallable) {
|
||||
is JavaConstructor -> javaCallable.valueParameters.getOrNull(idx)
|
||||
is JavaMethod -> javaCallable.valueParameters.getOrNull(idx)
|
||||
else -> null
|
||||
}
|
||||
val extraAnnotations =
|
||||
@@ -2874,6 +2934,52 @@ open class KotlinFileExtractor(
|
||||
return v
|
||||
}
|
||||
|
||||
private val sourceTextCache = mutableMapOf<String, String?>()
|
||||
|
||||
private fun getCurrentFileSourceText() =
|
||||
sourceTextCache.getOrPut(filePath) {
|
||||
runCatching { Files.readString(Path.of(filePath)) }.getOrNull()
|
||||
}
|
||||
|
||||
private fun getVariableNameLocation(v: IrVariable): Label<DbLocation>? {
|
||||
if (v.startOffset < 0 || v.endOffset < v.startOffset) return null
|
||||
|
||||
val source = getCurrentFileSourceText() ?: return null
|
||||
if (v.startOffset >= source.length) return null
|
||||
|
||||
val name = v.name.asString()
|
||||
if (name.isEmpty()) return null
|
||||
|
||||
val endExclusive = minOf(v.endOffset + 1, source.length)
|
||||
val declarationText = source.substring(v.startOffset, endExclusive)
|
||||
val nameOffsetInDeclaration = declarationText.indexOf(name)
|
||||
if (nameOffsetInDeclaration < 0) return null
|
||||
|
||||
val nameStartOffset = v.startOffset + nameOffsetInDeclaration
|
||||
// getLocation treats the end offset as exclusive (matching IR's getEndOffset), so the
|
||||
// identifier span is [nameStartOffset, nameStartOffset + name.length).
|
||||
val nameEndOffset = nameStartOffset + name.length
|
||||
return tw.getLocation(nameStartOffset, nameEndOffset)
|
||||
}
|
||||
|
||||
private fun shouldUseVariableNameLocation(v: IrVariable): Boolean {
|
||||
// For a variable initialised by an IMPLICIT_NOTNULL coercion (a platform-type not-null
|
||||
// assertion), the K2 frontend widens the IrVariable span to cover the coercion, which would
|
||||
// shift the location away from the identifier. Anchor those to the name token instead.
|
||||
// Variables without this coercion keep the location-provider span, which already points at
|
||||
// the identifier.
|
||||
val initializer = v.initializer
|
||||
return initializer is IrTypeOperatorCall && initializer.operator == IrTypeOperator.IMPLICIT_NOTNULL
|
||||
}
|
||||
|
||||
private fun getVariableLocation(v: IrVariable): Label<DbLocation> {
|
||||
if (shouldUseVariableNameLocation(v)) {
|
||||
val nameLocation = getVariableNameLocation(v)
|
||||
if (nameLocation != null) return nameLocation
|
||||
}
|
||||
return tw.getLocation(getVariableLocationProvider(v))
|
||||
}
|
||||
|
||||
private fun extractVariable(
|
||||
v: IrVariable,
|
||||
callable: Label<out DbCallable>,
|
||||
@@ -2882,7 +2988,7 @@ open class KotlinFileExtractor(
|
||||
) {
|
||||
with("variable", v) {
|
||||
val stmtId = tw.getFreshIdLabel<DbLocalvariabledeclstmt>()
|
||||
val locId = tw.getLocation(getVariableLocationProvider(v))
|
||||
val locId = getVariableLocation(v)
|
||||
tw.writeStmts_localvariabledeclstmt(stmtId, parent, idx, callable)
|
||||
tw.writeHasLocation(stmtId, locId)
|
||||
extractVariableExpr(v, callable, stmtId, 1, stmtId)
|
||||
@@ -2900,7 +3006,7 @@ open class KotlinFileExtractor(
|
||||
with("variable expr", v) {
|
||||
val varId = useVariable(v)
|
||||
val exprId = tw.getFreshIdLabel<DbLocalvariabledeclexpr>()
|
||||
val locId = tw.getLocation(getVariableLocationProvider(v))
|
||||
val locId = getVariableLocation(v)
|
||||
val type = useType(v.type)
|
||||
tw.writeLocalvars(varId, v.name.asString(), type.javaResult.id, exprId)
|
||||
tw.writeLocalvarsKotlinType(varId, type.kotlinResult.id)
|
||||
@@ -4066,6 +4172,28 @@ open class KotlinFileExtractor(
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun getCallResultType(c: IrCall, syntacticCallTarget: IrFunction): IrType {
|
||||
if (syntacticCallTarget.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB) {
|
||||
return c.type
|
||||
}
|
||||
|
||||
val primitiveInfo =
|
||||
(c.type as? IrSimpleType)?.let { primitiveTypeMapping.getPrimitiveInfo(it) } ?: return c.type
|
||||
val parentClass = syntacticCallTarget.parentClassOrNull ?: return c.type
|
||||
val returnIsClassifier =
|
||||
javaBinaryMethodReturnIsClassifierType(
|
||||
parentClass,
|
||||
getFunctionShortName(syntacticCallTarget).nameInDB,
|
||||
syntacticCallTarget.codeQlValueParameters.size,
|
||||
syntacticCallTarget is IrConstructor
|
||||
)
|
||||
return if (returnIsClassifier == true) {
|
||||
primitiveInfo.javaClass.symbol.typeWith()
|
||||
} else {
|
||||
c.type
|
||||
}
|
||||
}
|
||||
|
||||
private fun isGenericArrayType(typeName: String) =
|
||||
when (typeName) {
|
||||
"Array" -> true
|
||||
@@ -4111,7 +4239,7 @@ open class KotlinFileExtractor(
|
||||
extractRawMethodAccess(
|
||||
syntacticCallTarget,
|
||||
c,
|
||||
c.type,
|
||||
getCallResultType(c, syntacticCallTarget),
|
||||
callable,
|
||||
parent,
|
||||
idx,
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
|
||||
import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.NameUtils
|
||||
@@ -996,7 +997,20 @@ open class KotlinUsesExtractor(
|
||||
)
|
||||
return null
|
||||
}
|
||||
return extractFileClass(fqName)
|
||||
val fileClassId = extractFileClass(fqName)
|
||||
// Under K2, external file class members sit directly under IrExternalPackageFragment
|
||||
// rather than under their IrClass parent. In that case the file class entity won't
|
||||
// get a location set through the normal extractClassSource path.
|
||||
if (d is IrMemberWithContainerSource && tw.lm.externalFileClassLocationsExtracted.add(fqName)) {
|
||||
val binaryPath =
|
||||
getContainerSourceBinaryPath(d.containerSource)
|
||||
?.let { normalizeExternalFileClassBinaryPath(it, fqName) }
|
||||
if (binaryPath != null && shouldUseConcreteExternalFileClassLocation(binaryPath)) {
|
||||
val fileId = tw.mkFileId(binaryPath, true)
|
||||
tw.writeHasLocation(fileClassId, tw.getWholeFileLocation(fileId))
|
||||
}
|
||||
}
|
||||
return fileClassId
|
||||
}
|
||||
return useDeclarationParent(parent, canBeTopLevel, classTypeArguments, inReceiverContext)
|
||||
}
|
||||
@@ -1371,8 +1385,13 @@ open class KotlinUsesExtractor(
|
||||
parentId: Label<out DbElement>,
|
||||
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?,
|
||||
maybeParameterList: List<IrValueParameter>? = null
|
||||
): String =
|
||||
getFunctionLabel(
|
||||
): String {
|
||||
val javaCallable = getJavaCallable(f)
|
||||
val addParameterWildcardsByDefault =
|
||||
!getInnermostWildcardSupppressionAnnotation(f) &&
|
||||
!(javaCallable == null && f.origin == IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB)
|
||||
|
||||
return getFunctionLabel(
|
||||
f.parent,
|
||||
parentId,
|
||||
getFunctionShortName(f).nameInDB,
|
||||
@@ -1382,9 +1401,10 @@ open class KotlinUsesExtractor(
|
||||
getFunctionTypeParameters(f),
|
||||
classTypeArgsIncludingOuterClasses,
|
||||
overridesCollectionsMethodWithAlteredParameterTypes(f),
|
||||
getJavaCallable(f),
|
||||
!getInnermostWildcardSupppressionAnnotation(f)
|
||||
javaCallable,
|
||||
addParameterWildcardsByDefault
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* This function actually generates the label for a function.
|
||||
@@ -1471,15 +1491,41 @@ open class KotlinUsesExtractor(
|
||||
// Finally, mimic the Java extractor's behaviour by naming functions with type
|
||||
// parameters for their erased types;
|
||||
// those without type parameters are named for the generic type.
|
||||
val maybeErased =
|
||||
var maybeErased =
|
||||
if (functionTypeParameters.isEmpty()) maybeSubbed else erase(maybeSubbed)
|
||||
// K2 compatibility: under K2, Java @NotNull reference types such as @NotNull Integer
|
||||
// are enhanced to Kotlin primitives (e.g. kotlin.Int). But the Java extractor uses
|
||||
// the original reference type (java.lang.Integer) in callable labels. When we detect
|
||||
// that the original Java parameter type is a reference (classifier) type but the
|
||||
// Kotlin IR type is a primitive, revert to the boxed Java class so both extractors
|
||||
// produce matching callable IDs.
|
||||
if (functionTypeParameters.isEmpty()) {
|
||||
val primitiveInfo = (maybeErased as? IrSimpleType)?.let {
|
||||
primitiveTypeMapping.getPrimitiveInfo(it)
|
||||
}
|
||||
if (primitiveInfo != null) {
|
||||
val parentClass = parent as? IrClass
|
||||
if (parentClass != null) {
|
||||
val isClassifierType = javaBinaryMethodParamIsClassifierType(
|
||||
parentClass,
|
||||
name,
|
||||
allParamTypes.size,
|
||||
name == "<init>",
|
||||
it.index
|
||||
)
|
||||
if (isClassifierType == true) {
|
||||
maybeErased = primitiveInfo.javaClass.symbol.typeWith()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"{${useType(maybeErased).javaResult.id}}"
|
||||
}
|
||||
val paramTypeIds =
|
||||
allParamTypes
|
||||
.withIndex()
|
||||
.joinToString(separator = ",", transform = getIdForFunctionLabel)
|
||||
val labelReturnType =
|
||||
var labelReturnType =
|
||||
if (name == "<init>") pluginContext.irBuiltIns.unitType
|
||||
else
|
||||
erase(
|
||||
@@ -1489,6 +1535,28 @@ open class KotlinUsesExtractor(
|
||||
pluginContext
|
||||
)
|
||||
)
|
||||
// K2 compatibility: same as for parameters, if the Java binary method return type is a
|
||||
// reference type but K2 enhanced it to a Kotlin primitive, use the boxed Java class.
|
||||
if (functionTypeParameters.isEmpty() && name != "<init>") {
|
||||
val primitiveInfo = (labelReturnType as? IrSimpleType)?.let {
|
||||
primitiveTypeMapping.getPrimitiveInfo(it)
|
||||
}
|
||||
if (primitiveInfo != null) {
|
||||
val parentClass = parent as? IrClass
|
||||
if (parentClass != null) {
|
||||
val returnIsClassifier =
|
||||
javaBinaryMethodReturnIsClassifierType(
|
||||
parentClass,
|
||||
name,
|
||||
allParamTypes.size,
|
||||
false
|
||||
)
|
||||
if (returnIsClassifier == true) {
|
||||
labelReturnType = primitiveInfo.javaClass.symbol.typeWith()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Note that `addJavaLoweringWildcards` is not required here because the return type used to
|
||||
// form the function
|
||||
// label is always erased.
|
||||
@@ -1594,9 +1662,23 @@ open class KotlinUsesExtractor(
|
||||
}
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
fun getJavaCallable(f: IrFunction) =
|
||||
(f.descriptor.source as? JavaSourceElement)?.javaElement as? JavaMember
|
||||
fun getJavaCallable(f: IrFunction): JavaMember? {
|
||||
val fromDescriptor = (f.descriptor.source as? JavaSourceElement)?.javaElement as? JavaMember
|
||||
if (fromDescriptor != null) return fromDescriptor
|
||||
|
||||
// K2 fallback: under K2, descriptor.source may not carry JavaSourceElement for binary Java
|
||||
// methods. Try to get the JavaMember from the parent class's binary class directly.
|
||||
val parentClass = f.parentClassOrNull ?: return null
|
||||
val binaryJavaClass = (parentClass.source as? JavaSourceElement)?.javaElement as? BinaryJavaClass
|
||||
?: return null
|
||||
val name = getFunctionShortName(f).nameInDB
|
||||
val nParams = f.codeQlValueParameters.size
|
||||
return if (f is IrConstructor) {
|
||||
binaryJavaClass.constructors.find { it.valueParameters.size == nParams }
|
||||
} else {
|
||||
binaryJavaClass.methods.find { it.name.asString() == name && it.valueParameters.size == nParams }
|
||||
}
|
||||
}
|
||||
fun getJavaValueParameterType(m: JavaMember, idx: Int) =
|
||||
when (m) {
|
||||
is JavaMethod -> m.valueParameters[idx].type
|
||||
|
||||
@@ -51,6 +51,13 @@ class TrapLabelManager {
|
||||
* to avoid duplication.
|
||||
*/
|
||||
val fileClassLocationsExtracted = HashSet<IrFile>()
|
||||
|
||||
/**
|
||||
* Tracks external file classes (by FqName) whose location has been set from a binary path.
|
||||
* Used to avoid writing duplicate hasLocation facts for external file class entities extracted
|
||||
* through the K2 code path where declarations sit directly under IrExternalPackageFragment.
|
||||
*/
|
||||
val externalFileClassLocationsExtracted = HashSet<org.jetbrains.kotlin.name.FqName>()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
|
||||
import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
|
||||
|
||||
// Adapted from Kotlin's interpreter/Utils.kt function 'internalName'
|
||||
// Translates class names into their JLS section 13.1 binary name,
|
||||
@@ -176,15 +177,238 @@ fun getIrDeclarationBinaryPath(d: IrDeclaration): String? {
|
||||
// This is in a file class.
|
||||
val fqName = getFileClassFqName(d)
|
||||
if (fqName != null) {
|
||||
if (d is IrMemberWithContainerSource) {
|
||||
val containerBinaryPath = getContainerSourceBinaryPath(d.containerSource)
|
||||
if (containerBinaryPath != null) {
|
||||
return normalizeExternalFileClassBinaryPath(containerBinaryPath, fqName)
|
||||
}
|
||||
}
|
||||
return getUnknownBinaryLocation(fqName.asString())
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to get the binary file path from a container source (typically a
|
||||
* [JvmPackagePartSource]). Returns null if the path is unavailable.
|
||||
*/
|
||||
fun getContainerSourceBinaryPath(containerSource: org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource?): String? {
|
||||
if (containerSource !is JvmPackagePartSource) return null
|
||||
val binaryClass = containerSource.knownJvmBinaryClass ?: return null
|
||||
return when (binaryClass) {
|
||||
is VirtualFileKotlinClass -> {
|
||||
val vf = binaryClass.file
|
||||
val path = vf.path
|
||||
if (vf.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL)
|
||||
"/${path.split("!/", limit = 2)[1]}"
|
||||
else path
|
||||
}
|
||||
else -> binaryClass.location.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUnknownBinaryLocation(s: String): String {
|
||||
return "/!unknown-binary-location/${s.replace(".", "/")}.class"
|
||||
}
|
||||
|
||||
fun normalizeExternalFileClassBinaryPath(path: String, fqName: FqName): String {
|
||||
if (path.contains(".kotlinc_installed")) {
|
||||
return getUnknownBinaryLocation(fqName.asString())
|
||||
}
|
||||
val normalizedPath = path.replace('\\', '/')
|
||||
val classInternalPath = "${fqName.asString().replace(".", "/")}.class"
|
||||
val classSuffix = "/$classInternalPath"
|
||||
if (normalizedPath.endsWith(classSuffix)) {
|
||||
val classpathRoot = normalizedPath.removeSuffix(classSuffix).substringAfterLast('/')
|
||||
if (classpathRoot.isNotEmpty()) {
|
||||
return "$classpathRoot/$classInternalPath"
|
||||
}
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
fun shouldUseConcreteExternalFileClassLocation(path: String): Boolean {
|
||||
val normalizedPath = path.replace('\\', '/')
|
||||
return normalizedPath.contains("/") &&
|
||||
!normalizedPath.startsWith("/!unknown-binary-location/")
|
||||
}
|
||||
|
||||
fun getJavaEquivalentClassId(c: IrClass) =
|
||||
c.fqNameWhenAvailable?.toUnsafe()?.let { JavaToKotlinClassMap.mapKotlinToJava(it) }
|
||||
|
||||
/**
|
||||
* Checks whether a specific parameter of a Java binary method (identified by [methodName] and
|
||||
* [paramIndex]) is a reference type (as opposed to a Java primitive). This is used to detect
|
||||
* cases where K2 FIR has enhanced a reference type parameter (e.g. `@NotNull Integer`) to a
|
||||
* Kotlin primitive (e.g. `kotlin.Int`), so that callable labels can use the original reference
|
||||
* type and remain compatible with the Java extractor's callable IDs.
|
||||
*
|
||||
* Under K1, binary Java classes use [JavaSourceElement] and we can check [BinaryJavaClass.methods]
|
||||
* directly. Under K2, they use [VirtualFileBasedSourceElement] and we fall back to reading the
|
||||
* class bytes with ASM.
|
||||
*
|
||||
* Returns `null` if the information cannot be determined.
|
||||
*/
|
||||
fun javaBinaryMethodParamIsClassifierType(
|
||||
parentClass: IrClass,
|
||||
methodName: String,
|
||||
nParams: Int,
|
||||
isConstructor: Boolean,
|
||||
paramIndex: Int
|
||||
): Boolean? {
|
||||
// K1 path: binary Java class has JavaSourceElement with a BinaryJavaClass.
|
||||
val k1ParamKinds =
|
||||
((parentClass.source as? JavaSourceElement)?.javaElement as? BinaryJavaClass)?.let {
|
||||
binaryJavaClass ->
|
||||
if (isConstructor)
|
||||
binaryJavaClass.constructors
|
||||
.asSequence()
|
||||
.filter { it.valueParameters.size == nParams }
|
||||
.mapNotNull { it.valueParameters.getOrNull(paramIndex)?.type }
|
||||
.map { it is org.jetbrains.kotlin.load.java.structure.JavaClassifierType }
|
||||
.toSet()
|
||||
else
|
||||
binaryJavaClass.methods
|
||||
.asSequence()
|
||||
.filter { it.name.asString() == methodName && it.valueParameters.size == nParams }
|
||||
.mapNotNull { it.valueParameters.getOrNull(paramIndex)?.type }
|
||||
.map { it is org.jetbrains.kotlin.load.java.structure.JavaClassifierType }
|
||||
.toSet()
|
||||
}
|
||||
if (k1ParamKinds != null && k1ParamKinds.isNotEmpty()) {
|
||||
return k1ParamKinds.singleOrNull()
|
||||
}
|
||||
|
||||
// K2 path: binary Java class has VirtualFileBasedSourceElement
|
||||
val k2Source = parentClass.source as? VirtualFileBasedSourceElement ?: return null
|
||||
val vf = k2Source.virtualFile
|
||||
if (!vf.name.endsWith(".class")) return null
|
||||
|
||||
return try {
|
||||
val bytes = vf.contentsToByteArray()
|
||||
val expectedMethodName = if (isConstructor) "<init>" else methodName
|
||||
val descriptorKinds = mutableSetOf<Boolean>()
|
||||
val reader = org.jetbrains.org.objectweb.asm.ClassReader(bytes)
|
||||
reader.accept(
|
||||
object : org.jetbrains.org.objectweb.asm.ClassVisitor(
|
||||
org.jetbrains.org.objectweb.asm.Opcodes.ASM9
|
||||
) {
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
exceptions: Array<String>?
|
||||
): org.jetbrains.org.objectweb.asm.MethodVisitor? {
|
||||
if (name != expectedMethodName) return null
|
||||
val paramDescriptors = parseAsmMethodDescriptorParams(descriptor)
|
||||
if (paramDescriptors.size != nParams) return null
|
||||
val paramDesc = paramDescriptors.getOrNull(paramIndex) ?: return null
|
||||
// Reference types start with 'L' or '['; Java primitives are single chars
|
||||
descriptorKinds.add(paramDesc.startsWith("L") || paramDesc.startsWith("["))
|
||||
return null
|
||||
}
|
||||
},
|
||||
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_CODE or
|
||||
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_DEBUG or
|
||||
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_FRAMES
|
||||
)
|
||||
descriptorKinds.singleOrNull()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the return type of a Java binary method (identified by [methodName] and
|
||||
* [nParams]) is a reference type (as opposed to a Java primitive).
|
||||
*
|
||||
* Returns `null` if the information cannot be determined.
|
||||
*/
|
||||
fun javaBinaryMethodReturnIsClassifierType(
|
||||
parentClass: IrClass,
|
||||
methodName: String,
|
||||
nParams: Int,
|
||||
isConstructor: Boolean
|
||||
): Boolean? {
|
||||
if (isConstructor) return false
|
||||
|
||||
// K1 path: binary Java class has JavaSourceElement with a BinaryJavaClass.
|
||||
val k1ReturnKinds =
|
||||
((parentClass.source as? JavaSourceElement)?.javaElement as? BinaryJavaClass)?.methods
|
||||
?.asSequence()
|
||||
?.filter { it.name.asString() == methodName && it.valueParameters.size == nParams }
|
||||
?.map { it.returnType is org.jetbrains.kotlin.load.java.structure.JavaClassifierType }
|
||||
?.toSet()
|
||||
if (k1ReturnKinds != null && k1ReturnKinds.isNotEmpty()) {
|
||||
return k1ReturnKinds.singleOrNull()
|
||||
}
|
||||
|
||||
// K2 path: binary Java class has VirtualFileBasedSourceElement
|
||||
val k2Source = parentClass.source as? VirtualFileBasedSourceElement ?: return null
|
||||
val vf = k2Source.virtualFile
|
||||
if (!vf.name.endsWith(".class")) return null
|
||||
|
||||
return try {
|
||||
val bytes = vf.contentsToByteArray()
|
||||
val returnKinds = mutableSetOf<Boolean>()
|
||||
val reader = org.jetbrains.org.objectweb.asm.ClassReader(bytes)
|
||||
reader.accept(
|
||||
object : org.jetbrains.org.objectweb.asm.ClassVisitor(
|
||||
org.jetbrains.org.objectweb.asm.Opcodes.ASM9
|
||||
) {
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
exceptions: Array<String>?
|
||||
): org.jetbrains.org.objectweb.asm.MethodVisitor? {
|
||||
if (name != methodName) return null
|
||||
if (parseAsmMethodDescriptorParams(descriptor).size != nParams) return null
|
||||
val returnDescriptor = descriptor.substring(descriptor.lastIndexOf(')') + 1)
|
||||
returnKinds.add(
|
||||
returnDescriptor.startsWith("L") || returnDescriptor.startsWith("[")
|
||||
)
|
||||
return null
|
||||
}
|
||||
},
|
||||
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_CODE or
|
||||
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_DEBUG or
|
||||
org.jetbrains.org.objectweb.asm.ClassReader.SKIP_FRAMES
|
||||
)
|
||||
returnKinds.singleOrNull()
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseAsmMethodDescriptorParams(descriptor: String): List<String> {
|
||||
val params = mutableListOf<String>()
|
||||
var i = descriptor.indexOf('(') + 1
|
||||
val end = descriptor.lastIndexOf(')')
|
||||
while (i < end) {
|
||||
when (val c = descriptor[i]) {
|
||||
'L' -> {
|
||||
val semi = descriptor.indexOf(';', i)
|
||||
params.add(descriptor.substring(i, semi + 1))
|
||||
i = semi + 1
|
||||
}
|
||||
'[' -> {
|
||||
var j = i + 1
|
||||
while (j < end && descriptor[j] == '[') j++
|
||||
if (descriptor[j] == 'L') {
|
||||
val semi = descriptor.indexOf(';', j)
|
||||
params.add(descriptor.substring(i, semi + 1))
|
||||
i = semi + 1
|
||||
} else {
|
||||
params.add(descriptor.substring(i, j + 1))
|
||||
i = j + 1
|
||||
}
|
||||
}
|
||||
else -> { params.add(c.toString()); i++ }
|
||||
}
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
}
|
||||
rootProject.name = "Android Sample"
|
||||
|
||||
@@ -14,7 +14,9 @@ pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||
}
|
||||
}
|
||||
}
|
||||
rootProject.name = "Android Sample"
|
||||
|
||||
@@ -14,7 +14,9 @@ pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||
}
|
||||
}
|
||||
}
|
||||
rootProject.name = "Android Sample"
|
||||
|
||||
@@ -14,7 +14,9 @@ pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
}
|
||||
rootProject.name = "Android Sample"
|
||||
|
||||
@@ -13,7 +13,9 @@ buildscript {
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,6 +41,8 @@ buildscript {
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ buildscript {
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,6 +41,8 @@ buildscript {
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ buildscript {
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,6 +41,8 @@ buildscript {
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ buildscript {
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -32,13 +34,15 @@ buildscript {
|
||||
* dependencies used by all modules in your project, such as third-party plugins
|
||||
* or libraries. However, you should configure module-specific dependencies in
|
||||
* each module-level build.gradle file. For new projects, Android Studio
|
||||
* includes JCenter and Google's Maven repository by default, but it does not
|
||||
* includes Maven Central and Google's Maven repository by default, but it does not
|
||||
* configure any dependencies (unless you select a template that requires some).
|
||||
*/
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
}
|
||||
rootProject.name = "Android Sample"
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
apply plugin: 'java-library'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
https://repo.maven.apache.org/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar
|
||||
https://repo.maven.apache.org/maven2/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar
|
||||
https://repo.maven.apache.org/maven2/org/junit/jupiter/junit-jupiter-api/5.12.1/junit-jupiter-api-5.12.1.jar
|
||||
https://repo.maven.apache.org/maven2/org/junit/platform/junit-platform-commons/1.12.1/junit-platform-commons-1.12.1.jar
|
||||
https://repo.maven.apache.org/maven2/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/org/junit/jupiter/junit-jupiter-api/5.12.1/junit-jupiter-api-5.12.1.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/org/junit/platform/junit-platform-commons/1.12.1/junit-platform-commons-1.12.1.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
apply plugin: 'java-library'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
https://repo.maven.apache.org/maven2/joda-time/joda-time/2.12.7/joda-time-2.12.7-no-tzdb.jar
|
||||
https://repo.maven.apache.org/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/joda-time/joda-time/2.12.7/joda-time-2.12.7-no-tzdb.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar
|
||||
|
||||
@@ -12,9 +12,9 @@ apply plugin: 'java'
|
||||
|
||||
// In this section you declare where to find the dependencies of your project
|
||||
repositories {
|
||||
// Use 'jcenter' for resolving your dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
// In this section you declare the dependencies for your production and test code
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
apply plugin: 'java-library'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -1 +1 @@
|
||||
https://repo.maven.apache.org/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
apply plugin: 'java-library'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -1 +1 @@
|
||||
https://repo.maven.apache.org/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/org/apache/commons/commons-math3/3.6.1/commons-math3-3.6.1.jar
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
https://jcenter.bintray.com/junit/junit/4.12/junit-4.12.jar
|
||||
https://jcenter.bintray.com/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar
|
||||
https://jcenter.bintray.com/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.12/junit-4.12.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar
|
||||
https://maven-central.storage-download.googleapis.com/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar
|
||||
https://repo.maven.apache.org/maven2/com/feiniaojin/naaf/naaf-graceful-response-example/1.0/naaf-graceful-response-example-1.0.jar
|
||||
https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/avro-registry-in-source-tests/1.8/avro-registry-in-source-tests-1.8.jar
|
||||
https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/example-project/1.5/example-project-1.5.jar
|
||||
@@ -12,7 +13,6 @@ https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-example_2.11/0.1.2/r
|
||||
https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-java-example_2.11/0.1.2/rx-redis-java-example_2.11-0.1.2.jar
|
||||
https://repo.maven.apache.org/maven2/io/github/scrollsyou/example-spring-boot-starter/1.0.0/example-spring-boot-starter-1.0.0.jar
|
||||
https://repo.maven.apache.org/maven2/io/streamnative/com/example/maven-central-template/server/3.0.0/server-3.0.0.jar
|
||||
https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.jar
|
||||
https://repo.maven.apache.org/maven2/no/nav/security/token-validation-ktor-demo/3.1.0/token-validation-ktor-demo-3.1.0.jar
|
||||
https://repo.maven.apache.org/maven2/org/minijax/minijax-example-fileupload/0.5.10/minijax-example-fileupload-0.5.10.jar
|
||||
https://repo.maven.apache.org/maven2/org/minijax/minijax-example-inject/0.5.10/minijax-example-inject-0.5.10.jar
|
||||
|
||||
@@ -12,9 +12,9 @@ apply plugin: 'java'
|
||||
|
||||
// In this section you declare where to find the dependencies of your project
|
||||
repositories {
|
||||
// Use 'jcenter' for resolving your dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
// In this section you declare the dependencies for your production and test code
|
||||
|
||||
@@ -12,9 +12,9 @@ apply plugin: 'java'
|
||||
|
||||
// In this section you declare where to find the dependencies of your project
|
||||
repositories {
|
||||
// Use 'jcenter' for resolving your dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
// In this section you declare the dependencies for your production and test code
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<settings>
|
||||
<mirrors>
|
||||
<mirror>
|
||||
<id>google-maven-central</id>
|
||||
<name>GCS Maven Central mirror</name>
|
||||
<url>https://maven-central.storage-download.googleapis.com/maven2/</url>
|
||||
<mirrorOf>central</mirrorOf>
|
||||
</mirror>
|
||||
</mirrors>
|
||||
</settings>
|
||||
@@ -26,4 +26,5 @@ maven-project-2/src/main/resources/my-app.properties
|
||||
maven-project-2/src/main/resources/page.xml
|
||||
maven-project-2/src/main/resources/struts.xml
|
||||
maven-project-2/src/test/java/com/example/AppTest4.java
|
||||
settings.xml
|
||||
test-db/working/settings.xml
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import os
|
||||
|
||||
def test(codeql, use_java_11, java, actions_toolchains_file, check_diagnostics_java):
|
||||
# The version of gradle used doesn't work on java 17
|
||||
codeql.database.create(
|
||||
@@ -5,5 +7,6 @@ def test(codeql, use_java_11, java, actions_toolchains_file, check_diagnostics_j
|
||||
"CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true",
|
||||
"CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_CLASSPATH_FROM_BUILD_FILES": "true",
|
||||
"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file),
|
||||
"LGTM_INDEX_MAVEN_SETTINGS_FILE": os.path.join(os.path.dirname(os.path.realpath(__file__)), "settings.xml"),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -14,7 +14,9 @@ pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
}
|
||||
rootProject.name = "Android Sample"
|
||||
|
||||
@@ -12,9 +12,9 @@ apply plugin: 'java'
|
||||
|
||||
// In this section you declare where to find the dependencies of your project
|
||||
repositories {
|
||||
// Use 'jcenter' for resolving your dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
// In this section you declare the dependencies for your production and test code
|
||||
|
||||
@@ -12,9 +12,9 @@ apply plugin: 'java'
|
||||
|
||||
// In this section you declare where to find the dependencies of your project
|
||||
repositories {
|
||||
// Use 'jcenter' for resolving your dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
// In this section you declare the dependencies for your production and test code
|
||||
|
||||
@@ -12,8 +12,9 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
// Use Maven Central for resolving dependencies.
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -12,9 +12,9 @@ apply plugin: 'java'
|
||||
|
||||
// In this section you declare where to find the dependencies of your project
|
||||
repositories {
|
||||
// Use 'jcenter' for resolving your dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
// In this section you declare the dependencies for your production and test code
|
||||
|
||||
@@ -12,9 +12,9 @@ apply plugin: 'java'
|
||||
|
||||
// In this section you declare where to find the dependencies of your project
|
||||
repositories {
|
||||
// Use 'jcenter' for resolving your dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
// In this section you declare the dependencies for your production and test code
|
||||
|
||||
@@ -12,9 +12,9 @@ apply plugin: 'java'
|
||||
|
||||
// In this section you declare where to find the dependencies of your project
|
||||
repositories {
|
||||
// Use 'jcenter' for resolving your dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
// In this section you declare the dependencies for your production and test code
|
||||
|
||||
@@ -12,9 +12,9 @@ apply plugin: 'java'
|
||||
|
||||
// In this section you declare where to find the dependencies of your project
|
||||
repositories {
|
||||
// Use 'jcenter' for resolving your dependencies.
|
||||
// You can declare any Maven/Ivy/file repository here.
|
||||
jcenter()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
// In this section you declare the dependencies for your production and test code
|
||||
|
||||
@@ -11,7 +11,9 @@ version = '0.0.1-SNAPSHOT'
|
||||
// but I omit it to test we recognise the Spring Boot plugin version.
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -15,8 +15,9 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
// Use Maven Central for resolving dependencies.
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import pathlib
|
||||
|
||||
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
def test(codeql, java_full):
|
||||
java_srcs = " ".join([str(s) for s in pathlib.Path().glob("*.java")])
|
||||
codeql.database.create(
|
||||
command=[
|
||||
f"javac {java_srcs} -d build",
|
||||
"kotlinc -language-version 1.9 user.kt -cp build",
|
||||
"kotlinc -language-version 2.0 user.kt -cp build",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import commands
|
||||
|
||||
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
commands.run("kotlinc -language-version 1.9 test.kt -d lib")
|
||||
codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp lib")
|
||||
def test(codeql, java_full):
|
||||
commands.run("kotlinc -language-version 2.0 test.kt -d lib")
|
||||
codeql.database.create(command="kotlinc -language-version 2.0 user.kt -cp lib")
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
| Percentage of calls with call target | 100 |
|
||||
| Total number of lines | 3 |
|
||||
| Total number of lines with extension kt | 3 |
|
||||
| Uses Kotlin 2: false | 1 |
|
||||
| Uses Kotlin 2: true | 1 |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
codeql.database.create(command=f"kotlinc -J-Xmx2G -language-version 1.9 SomeClass.kt")
|
||||
def test(codeql, java_full):
|
||||
codeql.database.create(command="kotlinc -J-Xmx2G -language-version 2.0 SomeClass.kt")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import commands
|
||||
|
||||
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
commands.run("kotlinc -language-version 1.9 A.kt")
|
||||
codeql.database.create(command="kotlinc -cp . -language-version 1.9 B.kt C.kt")
|
||||
def test(codeql, java_full):
|
||||
commands.run("kotlinc -language-version 2.0 A.kt")
|
||||
codeql.database.create(command="kotlinc -cp . -language-version 2.0 B.kt C.kt")
|
||||
|
||||
@@ -15,8 +15,9 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
// Use Maven Central for resolving dependencies.
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
|
||||
@@ -4,7 +4,9 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import commands
|
||||
|
||||
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
def test(codeql, java_full):
|
||||
commands.run(["javac", "Test.java", "-d", "bin"])
|
||||
codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp bin")
|
||||
codeql.database.create(command="kotlinc -language-version 2.0 user.kt -cp bin")
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import commands
|
||||
|
||||
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
def test(codeql, java_full):
|
||||
# Compile the JavaDefns2 copy outside tracing, to make sure the Kotlin view of it matches the Java view seen by the traced javac compilation of JavaDefns.java below.
|
||||
commands.run(["javac", "JavaDefns2.java"])
|
||||
codeql.database.create(
|
||||
command=[
|
||||
"kotlinc kotlindefns.kt",
|
||||
"javac JavaUser.java JavaDefns.java -cp .",
|
||||
"kotlinc -language-version 1.9 -cp . kotlinuser.kt",
|
||||
"kotlinc -language-version 2.0 -cp . kotlinuser.kt",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -15,8 +15,9 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
// Use Maven Central for resolving dependencies.
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-all
|
||||
version: 9.2.0
|
||||
version: 9.2.1-dev
|
||||
groups: java
|
||||
dbscheme: config/semmlecode.dbscheme
|
||||
extractor: java
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-queries
|
||||
version: 1.11.5
|
||||
version: 1.11.6-dev
|
||||
groups:
|
||||
- java
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added support for Angular's `@HostListener('window:message', ...)` and `@HostListener('document:message', ...)` decorators as `postMessage` event handlers. The decorated method's event parameter is now recognized as a client-side remote flow source, and is considered by the `js/missing-origin-check` query.
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-all
|
||||
version: 2.8.0
|
||||
version: 2.8.1-dev
|
||||
groups: javascript
|
||||
dbscheme: semmlecode.javascript.dbscheme
|
||||
extractor: javascript
|
||||
|
||||
@@ -195,6 +195,18 @@ class PostMessageEventHandler extends Function {
|
||||
rhs = DataFlow::globalObjectRef().getAPropertyWrite("onmessage").getRhs() and
|
||||
rhs.getABoundFunctionValue(paramIndex).getFunction() = this
|
||||
)
|
||||
or
|
||||
// Angular's `@HostListener('window:message', ['$event'])` decorator registers
|
||||
// a method as a `message` event handler on the global `window` or `document`
|
||||
// target. The decorated method receives the `MessageEvent` as its first
|
||||
// parameter, so it is equivalent to `window.addEventListener('message', ...)`.
|
||||
exists(MethodDefinition method, DataFlow::CallNode decorator |
|
||||
decorator = DataFlow::moduleMember("@angular/core", "HostListener").getACall() and
|
||||
decorator = method.getADecorator().getExpression().flow() and
|
||||
decorator.getArgument(0).mayHaveStringValue(["window:message", "document:message"]) and
|
||||
method.getBody() = this and
|
||||
paramIndex = 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-queries
|
||||
version: 2.4.0
|
||||
version: 2.4.1-dev
|
||||
groups:
|
||||
- javascript
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Component, HostListener } from '@angular/core';
|
||||
|
||||
@Component({ selector: 'app-root' })
|
||||
class AngularComponent {
|
||||
// Angular registers this as a `window` message handler via the decorator,
|
||||
// equivalent to `window.addEventListener('message', ...)`.
|
||||
@HostListener('window:message', ['$event'])
|
||||
onWindowMessage(event: MessageEvent): void { // $ Alert - no origin check
|
||||
eval(event.data);
|
||||
}
|
||||
|
||||
@HostListener('document:message', ['$event'])
|
||||
onDocumentMessage(event: MessageEvent): void { // $ Alert - no origin check
|
||||
eval(event.data);
|
||||
}
|
||||
|
||||
@HostListener('window:message', ['$event'])
|
||||
onCheckedMessage(event: MessageEvent): void { // OK - has an origin check
|
||||
if (event.origin === 'https://www.example.com') {
|
||||
eval(event.data);
|
||||
}
|
||||
}
|
||||
|
||||
// Not a message event, so it is not a postMessage handler.
|
||||
@HostListener('window:resize', ['$event'])
|
||||
onResize(event: MessageEvent): void { // OK - not a message handler
|
||||
eval(event.data);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
| Angular.ts:8:19:8:23 | event | Postmessage handler has no origin check. |
|
||||
| Angular.ts:13:21:13:25 | event | Postmessage handler has no origin check. |
|
||||
| tst.js:11:20:11:24 | event | Postmessage handler has no origin check. |
|
||||
| tst.js:24:27:24:27 | e | Postmessage handler has no origin check. |
|
||||
| tst.js:40:27:40:27 | e | Postmessage handler has no origin check. |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: codeql/suite-helpers
|
||||
version: 1.0.52
|
||||
version: 1.0.53-dev
|
||||
groups: shared
|
||||
warnOnImplicitThis: true
|
||||
|
||||
@@ -70,7 +70,7 @@ No user-facing changes.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added new full SSRF sanitization barrier from the new AntiSSRF library.
|
||||
* Added new full SSRF sanitization barrier from the new AntiSSRF library.
|
||||
* When a guard such as `isSafe(x)` is defined, we now also automatically handle `isSafe(x) == true` and `isSafe(x) != false`.
|
||||
|
||||
## 6.1.1
|
||||
@@ -169,7 +169,7 @@ No user-facing changes.
|
||||
### Minor Analysis Improvements
|
||||
|
||||
- The modelling of Psycopg2 now supports the use of `psycopg2.pool` connection pools for handling database connections.
|
||||
* Removed `lxml` as an XML bomb sink. The underlying libxml2 library now includes [entity reference loop detection](https://github.com/lxml/lxml/blob/f33ac2c2f5f9c4c4c1fc47f363be96db308f2fa6/doc/FAQ.txt#L1077) that prevents XML bomb attacks.
|
||||
* Removed `lxml` as an XML bomb sink. The underlying libxml2 library now includes [entity reference loop detection](https://github.com/lxml/lxml/blob/f33ac2c2f5f9c4c4c1fc47f363be96db308f2fa6/doc/FAQ.txt#L1077) that prevents XML bomb attacks.
|
||||
|
||||
## 4.0.13
|
||||
|
||||
@@ -262,7 +262,7 @@ No user-facing changes.
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The sensitive data library has been improved so that `snake_case` style variable names are recognized more reliably. This may result in more sensitive data being identified, and more results from queries that use the sensitive data library.
|
||||
- Additional taint steps through methods of `lxml.etree.Element` and `lxml.etree.ElementTree` objects from the `lxml` PyPI package have been modeled.
|
||||
- Additional taint steps through methods of `lxml.etree.Element` and `lxml.etree.ElementTree` objects from the `lxml` PyPI package have been modeled.
|
||||
|
||||
## 3.1.0
|
||||
|
||||
@@ -316,7 +316,7 @@ No user-facing changes.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The common sanitizer guard `StringConstCompareBarrier` has been renamed to `ConstCompareBarrier` and expanded to cover comparisons with other constant values such as `None`. This may result in fewer false positive results for several queries.
|
||||
* The common sanitizer guard `StringConstCompareBarrier` has been renamed to `ConstCompareBarrier` and expanded to cover comparisons with other constant values such as `None`. This may result in fewer false positive results for several queries.
|
||||
|
||||
## 2.0.0
|
||||
|
||||
@@ -545,7 +545,7 @@ No user-facing changes.
|
||||
|
||||
### New Features
|
||||
|
||||
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
|
||||
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
|
||||
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
@@ -572,7 +572,7 @@ No user-facing changes.
|
||||
* Deleted many deprecated predicates and classes with uppercase `API`, `HTTP`, `XSS`, `SQL`, etc. in their names. Use the PascalCased versions instead.
|
||||
* Deleted the deprecated `getName()` predicate from the `Container` class, use `getAbsolutePath()` instead.
|
||||
* Deleted many deprecated module names that started with a lowercase letter, use the versions that start with an uppercase letter instead.
|
||||
* Deleted many deprecated predicates in `PointsTo.qll`.
|
||||
* Deleted many deprecated predicates in `PointsTo.qll`.
|
||||
* Deleted many deprecated files from the `semmle.python.security` package.
|
||||
* Deleted the deprecated `BottleRoutePointToExtension` class from `Extensions.qll`.
|
||||
* Type tracking is now aware of flow summaries. This leads to a richer API graph, and may lead to more results in some queries.
|
||||
@@ -729,7 +729,7 @@ No user-facing changes.
|
||||
### Deprecated APIs
|
||||
|
||||
* Some unused predicates in `SsaDefinitions.qll`, `TObject.qll`, `protocols.qll`, and the `pointsto/` folder have been deprecated.
|
||||
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
@@ -748,9 +748,9 @@ No user-facing changes.
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
* The utility files previously in the `semmle.python.security.performance` package have been moved to the `semmle.python.security.regexp` package.
|
||||
* The utility files previously in the `semmle.python.security.performance` package have been moved to the `semmle.python.security.regexp` package.
|
||||
The previous files still exist as deprecated aliases.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
@@ -843,9 +843,9 @@ No user-facing changes.
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules that had upper-case acronyms have been renamed to follow our style-guide.
|
||||
* Many classes/predicates/modules that had upper-case acronyms have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
* Some modules that started with a lowercase letter have been renamed to follow our style-guide.
|
||||
* Some modules that started with a lowercase letter have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* `Flask::FlaskApp::instance()` will now also return instances of subclasses defined in the source tree. Previously, these were filtered out. `Flask::FlaskApp::classRef()` has been deprecated in favor of `Flask::FlaskApp::subclassRef()` since it already returned some subclasses.
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-all
|
||||
version: 7.2.0
|
||||
version: 7.2.1-dev
|
||||
groups: python
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
|
||||
@@ -1138,9 +1138,7 @@ predicate clearsContent(Node n, ContentSet cs) {
|
||||
* Holds if the value that is being tracked is expected to be stored inside content `c`
|
||||
* at node `n`.
|
||||
*/
|
||||
predicate expectsContent(Node n, ContentSet c) {
|
||||
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
|
||||
}
|
||||
predicate expectsContent(Node n, ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Holds if values stored inside attribute `c` are cleared at node `n`.
|
||||
|
||||
@@ -91,8 +91,6 @@ module Input implements InputSig<Location, DataFlowImplSpecific::PythonDataFlow>
|
||||
cs.isAnyTupleOrDictionaryElement() and result = "AnyTupleOrDictionaryElement" and arg = ""
|
||||
}
|
||||
|
||||
string encodeWithContent(ContentSet c, string arg) { result = "With" + encodeContent(c, arg) }
|
||||
|
||||
bindingset[token]
|
||||
ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) {
|
||||
// needed to support `Argument[x..y]` ranges
|
||||
|
||||
@@ -170,13 +170,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
||||
|
||||
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */
|
||||
predicate levelStepCall(Node nodeFrom, LocalSourceNode nodeTo) {
|
||||
// HOTFIX: `instanceFieldStep` is temporarily disabled (via `and none()`).
|
||||
// It uses `classInstanceTracker(cls)` -- itself a type-tracker run --
|
||||
// from inside `levelStepCall`, creating a structural mutual recursion
|
||||
// that causes catastrophic query slowdowns on some OOP-heavy Python
|
||||
// codebases (e.g. mypy and dask). The `and none()` should be removed
|
||||
// once that recursion is redesigned.
|
||||
instanceFieldStep(nodeFrom, nodeTo) and none()
|
||||
instanceFieldStep(nodeFrom, nodeTo)
|
||||
or
|
||||
inheritedFieldStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
@@ -71,14 +71,21 @@ module Flask {
|
||||
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.
|
||||
*/
|
||||
module FlaskApp {
|
||||
/** Gets a reference to the `flask.Flask` class. */
|
||||
API::Node classRef() {
|
||||
result = API::moduleImport("flask").getMember("Flask") or
|
||||
/**
|
||||
* Gets a reference to the `flask.Flask` class or any subclass.
|
||||
*
|
||||
* Deprecated: Use `subclassRef()` instead, this predicate always returned some subclasses.
|
||||
*/
|
||||
deprecated API::Node classRef() { result = subclassRef() }
|
||||
|
||||
/** Gets a reference to the `flask.Flask` class or any subclass. */
|
||||
API::Node subclassRef() {
|
||||
result = API::moduleImport("flask").getMember("Flask").getASubclass*() or
|
||||
result = ModelOutput::getATypeNode("flask.Flask~Subclass").getASubclass*()
|
||||
}
|
||||
|
||||
/** Gets a reference to an instance of `flask.Flask` (a flask application). */
|
||||
API::Node instance() { result = classRef().getReturn() }
|
||||
API::Node instance() { result = subclassRef().getReturn() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +139,7 @@ module Flask {
|
||||
API::Node classRef() {
|
||||
result = API::moduleImport("flask").getMember("Response")
|
||||
or
|
||||
result = [FlaskApp::classRef(), FlaskApp::instance()].getMember("response_class")
|
||||
result = [FlaskApp::subclassRef(), FlaskApp::instance()].getMember("response_class")
|
||||
or
|
||||
result = ModelOutput::getATypeNode("flask.Response~Subclass").getASubclass*()
|
||||
}
|
||||
|
||||
@@ -4199,9 +4199,11 @@ module StdlibPrivate {
|
||||
// The positional argument contains a mapping.
|
||||
// TODO: these values can be overwritten by keyword arguments
|
||||
// - dict mapping
|
||||
input = "Argument[0].WithAnyDictionaryElement" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||
input = "Argument[0].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue.DictionaryElement[" + key + "]" and
|
||||
preservesValue = true
|
||||
)
|
||||
or
|
||||
// - list-of-pairs mapping
|
||||
input = "Argument[0].ListElement.TupleElement[1]" and
|
||||
@@ -4238,7 +4240,9 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
// Element content is mutated into list element content
|
||||
@@ -4262,9 +4266,11 @@ module StdlibPrivate {
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[0].WithAnyTupleElement" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]" and
|
||||
output = "ReturnValue.TupleElement[" + i.toString() + "]" and
|
||||
preservesValue = true
|
||||
)
|
||||
or
|
||||
input = "Argument[0].ListElement" and
|
||||
output = "ReturnValue" and
|
||||
@@ -4288,7 +4294,9 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue.SetElement" and
|
||||
@@ -4334,7 +4342,9 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue.ListElement" and
|
||||
@@ -4362,7 +4372,9 @@ module StdlibPrivate {
|
||||
or
|
||||
content = "SetElement"
|
||||
or
|
||||
content = "AnyTupleElement"
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
content = "TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
|
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
input = "Argument[0]." + content and
|
||||
@@ -4392,7 +4404,9 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue.ListElement" and
|
||||
@@ -4420,7 +4434,9 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue" and
|
||||
@@ -4452,7 +4468,9 @@ module StdlibPrivate {
|
||||
// We reduce generality slightly by not tracking tuple contents on list arguments beyond the first, for performance.
|
||||
// TODO: Once we have TupleElementAny, this generality can be increased.
|
||||
i = 0 and
|
||||
input = "Argument[1].AnyTupleElement"
|
||||
exists(DataFlow::TupleElementContent tc, int j | j = tc.getIndex() |
|
||||
input = "Argument[1].TupleElement[" + j.toString() + "]"
|
||||
)
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "Argument[0].Parameter[" + i.toString() + "]" and
|
||||
@@ -4481,7 +4499,9 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[1].SetElement"
|
||||
or
|
||||
input = "Argument[1].AnyTupleElement"
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[1].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
(output = "Argument[0].Parameter[0]" or output = "ReturnValue.ListElement") and
|
||||
@@ -4505,7 +4525,9 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[0].SetElement"
|
||||
or
|
||||
input = "Argument[0].AnyTupleElement"
|
||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||
input = "Argument[0].TupleElement[" + i.toString() + "]"
|
||||
)
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue.ListElement.TupleElement[1]" and
|
||||
@@ -4530,7 +4552,12 @@ module StdlibPrivate {
|
||||
or
|
||||
input = "Argument[" + i.toString() + "].SetElement"
|
||||
or
|
||||
input = "Argument[" + i.toString() + "].AnyTupleElement"
|
||||
// We reduce generality slightly by not tracking tuple contents on arguments beyond the first two, for performance.
|
||||
// TODO: Once we have TupleElementAny, this generality can be increased.
|
||||
i in [0 .. 1] and
|
||||
exists(DataFlow::TupleElementContent tc, int j | j = tc.getIndex() |
|
||||
input = "Argument[" + i.toString() + "].TupleElement[" + j.toString() + "]"
|
||||
)
|
||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||
) and
|
||||
output = "ReturnValue.ListElement.TupleElement[" + i.toString() + "]" and
|
||||
@@ -4553,6 +4580,12 @@ module StdlibPrivate {
|
||||
override DataFlow::ArgumentNode getACallback() { none() }
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
exists(DataFlow::Content c |
|
||||
input = "Argument[self]." + c.getMaDRepresentation() and
|
||||
output = "ReturnValue." + c.getMaDRepresentation() and
|
||||
preservesValue = true
|
||||
)
|
||||
or
|
||||
input = "Argument[self]" and
|
||||
output = "ReturnValue" and
|
||||
preservesValue = true
|
||||
@@ -4708,10 +4741,12 @@ module StdlibPrivate {
|
||||
override DataFlow::ArgumentNode getACallback() { none() }
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[self].AnyDictionaryElement" and
|
||||
output = "ReturnValue.TupleElement[1]" and
|
||||
preservesValue = true
|
||||
// TODO: put `key` into "ReturnValue.TupleElement[0]"
|
||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue.TupleElement[1]" and
|
||||
preservesValue = true
|
||||
// TODO: put `key` into "ReturnValue.TupleElement[0]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4790,9 +4825,11 @@ module StdlibPrivate {
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[self].AnyDictionaryElement" and
|
||||
output = "ReturnValue.ListElement" and
|
||||
preservesValue = true
|
||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue.ListElement" and
|
||||
preservesValue = true
|
||||
)
|
||||
or
|
||||
input = "Argument[self]" and
|
||||
output = "ReturnValue" and
|
||||
@@ -4839,9 +4876,11 @@ module StdlibPrivate {
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
input = "Argument[self].AnyDictionaryElement" and
|
||||
output = "ReturnValue.ListElement.TupleElement[1]" and
|
||||
preservesValue = true
|
||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||
output = "ReturnValue.ListElement.TupleElement[1]" and
|
||||
preservesValue = true
|
||||
)
|
||||
or
|
||||
// TODO: Add the keys to output list
|
||||
input = "Argument[self]" and
|
||||
|
||||
@@ -351,7 +351,7 @@ class DjangoHttpRequest extends FindSubclassesSpec {
|
||||
class FlaskClass extends FindSubclassesSpec {
|
||||
FlaskClass() { this = "flask.Flask~Subclass" }
|
||||
|
||||
override API::Node getAlreadyModeledClass() { result = Flask::FlaskApp::classRef() }
|
||||
override API::Node getAlreadyModeledClass() { result = Flask::FlaskApp::subclassRef() }
|
||||
}
|
||||
|
||||
class FlaskBlueprint extends FindSubclassesSpec {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-queries
|
||||
version: 1.8.5
|
||||
version: 1.8.6-dev
|
||||
groups:
|
||||
- python
|
||||
- queries
|
||||
|
||||
29
python/ql/test/experimental/meta/InlineInstanceTest.qll
Normal file
29
python/ql/test/experimental/meta/InlineInstanceTest.qll
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Defines an InlineExpectationsTest for class instances, that is,
|
||||
* for any API::Node that is an instance of a class (e.g. `Flask`).
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
import utils.test.InlineExpectationsTest
|
||||
private import semmle.python.dataflow.new.internal.PrintNode
|
||||
|
||||
signature API::Node getInstanceSig();
|
||||
|
||||
module MakeInlineInstanceTest<getInstanceSig/0 getInstance> {
|
||||
private module InlineInstanceTest implements TestSig {
|
||||
string getARelevantTag() { result = "instance" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(location.getFile().getRelativePath()) and
|
||||
exists(API::Node instance | instance = getInstance() |
|
||||
location = instance.getLocation() and
|
||||
element = prettyNode(instance.asSource()) and
|
||||
value = "" and
|
||||
tag = "instance"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<InlineInstanceTest>
|
||||
}
|
||||
@@ -589,11 +589,11 @@ def test_zip_tuple():
|
||||
|
||||
SINK(z[0][0]) # $ flow="SOURCE, l:-7 -> z[0][0]"
|
||||
SINK(z[0][1]) # $ flow="SOURCE, l:-7 -> z[0][1]"
|
||||
SINK_F(z[0][2]) # $ SPURIOUS: flow="SOURCE, l:-7 -> z[0][2]"
|
||||
SINK_F(z[0][2])
|
||||
SINK_F(z[0][3])
|
||||
SINK(z[1][0]) # $ flow="SOURCE, l:-11 -> z[1][0]"
|
||||
SINK_F(z[1][1]) # $ SPURIOUS: flow="SOURCE, l:-11 -> z[1][1]"
|
||||
SINK(z[1][2]) # $ flow="SOURCE, l:-11 -> z[1][2]"
|
||||
SINK(z[1][2]) # $ MISSING: flow="SOURCE, l:-11 -> z[1][2]" # Tuple contents are not tracked beyond the first two arguments for performance.
|
||||
SINK_F(z[1][3])
|
||||
|
||||
@expects(4)
|
||||
|
||||
@@ -157,7 +157,7 @@ class MyClass2(object):
|
||||
print(self.foo) # $ tracked MISSING: tracked=foo
|
||||
|
||||
instance = MyClass2()
|
||||
print(instance.foo) # $ MISSING: tracked=foo tracked
|
||||
print(instance.foo) # $ tracked MISSING: tracked=foo
|
||||
instance.print_foo() # $ MISSING: tracked=foo
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ class Sub1(Base1):
|
||||
|
||||
sub1 = Sub1()
|
||||
sub1.read_foo()
|
||||
print(sub1.foo) # $ MISSING: tracked=foo tracked
|
||||
print(sub1.foo) # $ tracked MISSING: tracked=foo
|
||||
|
||||
|
||||
# attribute written in a subclass method, read in an inherited base class method
|
||||
@@ -210,7 +210,7 @@ class Sub2(Base2):
|
||||
|
||||
sub2 = Sub2()
|
||||
sub2.read_bar()
|
||||
print(sub2.bar) # $ MISSING: tracked=bar tracked
|
||||
print(sub2.bar) # $ tracked MISSING: tracked=bar
|
||||
|
||||
|
||||
# attribute written in a base class method, read on an instance of the subclass
|
||||
@@ -223,4 +223,4 @@ class Sub3(Base3):
|
||||
pass
|
||||
|
||||
sub3 = Sub3()
|
||||
print(sub3.baz) # $ MISSING: tracked=baz tracked
|
||||
print(sub3.baz) # $ tracked MISSING: tracked=baz
|
||||
|
||||
@@ -362,7 +362,7 @@ def test_load_in_bulk():
|
||||
# see https://docs.djangoproject.com/en/4.0/ref/models/querysets/#in-bulk
|
||||
d = TestLoad.objects.in_bulk([1])
|
||||
for val in d.values():
|
||||
SINK(val.text) # $ flow="SOURCE, l:-65 -> val.text"
|
||||
SINK(val.text) # $ MISSING: flow
|
||||
SINK(d[1].text) # $ flow="SOURCE, l:-66 -> d[1].text"
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import python
|
||||
import semmle.python.frameworks.Flask
|
||||
import semmle.python.ApiGraphs
|
||||
import experimental.meta.InlineInstanceTest
|
||||
|
||||
API::Node getInstance() { result = Flask::FlaskApp::instance() }
|
||||
|
||||
import MakeInlineInstanceTest<getInstance/0>
|
||||
@@ -0,0 +1,14 @@
|
||||
from flask import Flask
|
||||
|
||||
|
||||
class Sub(Flask):
|
||||
def __init__(self, *args, **kwargs):
|
||||
Flask.__init__(self, *args, **kwargs)
|
||||
|
||||
|
||||
app = Sub(__name__) # $ instance
|
||||
|
||||
|
||||
@app.route("/") # $ routeSetup="/"
|
||||
def hello(): # $ requestHandler
|
||||
return "world" # $ HttpResponse
|
||||
@@ -1,7 +1,7 @@
|
||||
import flask
|
||||
|
||||
from flask import Flask, request, make_response
|
||||
app = Flask(__name__)
|
||||
app = Flask(__name__) # $ instance
|
||||
|
||||
@app.route("/") # $ routeSetup="/"
|
||||
def hello_world(): # $ requestHandler
|
||||
|
||||
@@ -3,7 +3,7 @@ import json
|
||||
from flask import Flask, make_response, jsonify, Response, request, redirect
|
||||
from werkzeug.datastructures import Headers
|
||||
|
||||
app = Flask(__name__)
|
||||
app = Flask(__name__) # $ instance
|
||||
|
||||
|
||||
@app.route("/html1") # $ routeSetup="/html1"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import flask
|
||||
|
||||
from flask import Flask, make_response
|
||||
app = Flask(__name__)
|
||||
app = Flask(__name__) # $ instance
|
||||
|
||||
|
||||
SOME_ROUTE = "/some/route"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from flask import Flask, request
|
||||
app = Flask(__name__)
|
||||
app = Flask(__name__) # $ instance
|
||||
|
||||
@app.route("/save-uploaded-file") # $ routeSetup="/save-uploaded-file"
|
||||
def test_taint(): # $ requestHandler
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from flask import Flask, request, render_template_string, stream_template_string
|
||||
app = Flask(__name__)
|
||||
app = Flask(__name__) # $ instance
|
||||
|
||||
@app.route("/test_taint/<name>/<int:number>") # $ routeSetup="/test_taint/<name>/<int:number>"
|
||||
def test_taint(name = "World!", number="0", foo="foo"): # $ requestHandler routedParameter=name routedParameter=number
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from flask import Flask, Response, stream_with_context, render_template_string, stream_template_string
|
||||
app = Flask(__name__)
|
||||
app = Flask(__name__) # $ instance
|
||||
|
||||
@app.route("/a") # $ routeSetup="/a"
|
||||
def a(): # $ requestHandler
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#select
|
||||
| app.py:23:20:23:24 | ControlFlowNode for query | app.py:20:18:20:21 | ControlFlowNode for name | app.py:23:20:23:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:20:18:20:21 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:30:20:30:24 | ControlFlowNode for query | app.py:27:19:27:22 | ControlFlowNode for name | app.py:30:20:30:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:27:19:27:22 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:37:20:37:24 | ControlFlowNode for query | app.py:34:19:34:22 | ControlFlowNode for name | app.py:37:20:37:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:34:19:34:22 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:44:20:44:24 | ControlFlowNode for query | app.py:41:19:41:22 | ControlFlowNode for name | app.py:44:20:44:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:41:19:41:22 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:51:20:51:24 | ControlFlowNode for query | app.py:48:19:48:22 | ControlFlowNode for name | app.py:51:20:51:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:48:19:48:22 | ControlFlowNode for name | user-provided value |
|
||||
| sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | This SQL query depends on a $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | user-provided value |
|
||||
@@ -24,6 +25,8 @@ edges
|
||||
| app.py:21:5:21:9 | ControlFlowNode for query | app.py:23:20:23:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:27:19:27:22 | ControlFlowNode for name | app.py:28:5:28:9 | ControlFlowNode for query | provenance | |
|
||||
| app.py:28:5:28:9 | ControlFlowNode for query | app.py:30:20:30:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:34:19:34:22 | ControlFlowNode for name | app.py:35:5:35:9 | ControlFlowNode for query | provenance | |
|
||||
| app.py:35:5:35:9 | ControlFlowNode for query | app.py:37:20:37:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:41:19:41:22 | ControlFlowNode for name | app.py:42:5:42:9 | ControlFlowNode for query | provenance | |
|
||||
| app.py:42:5:42:9 | ControlFlowNode for query | app.py:44:20:44:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:48:19:48:22 | ControlFlowNode for name | app.py:49:5:49:9 | ControlFlowNode for query | provenance | |
|
||||
@@ -51,6 +54,9 @@ nodes
|
||||
| app.py:27:19:27:22 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
|
||||
| app.py:28:5:28:9 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:30:20:30:24 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:34:19:34:22 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
|
||||
| app.py:35:5:35:9 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:37:20:37:24 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:41:19:41:22 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
|
||||
| app.py:42:5:42:9 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:44:20:44:24 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
|
||||
@@ -31,10 +31,10 @@ async def unsafe2(name: str): # $ Source
|
||||
cursor.close()
|
||||
|
||||
@app.get("/unsafe3/")
|
||||
async def unsafe3(name: str): # $ MISSING: Source
|
||||
async def unsafe3(name: str): # $ Source
|
||||
query = "select * from users where name=" + name
|
||||
cursor = hdb_con3.cursor()
|
||||
cursor.execute(query) # $ MISSING: Alert
|
||||
cursor.execute(query) # $ Alert
|
||||
cursor.close()
|
||||
|
||||
@app.get("/unsafe4/")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user