mirror of
https://github.com/github/codeql.git
synced 2026-07-04 19:15:29 +02:00
Compare commits
65 Commits
copilot/re
...
yoff/pytho
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aec819f3c9 | ||
|
|
47d2b05bc5 | ||
|
|
41c9d8b80a | ||
|
|
fbfbbd342a | ||
|
|
872c08148e | ||
|
|
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 |
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/actions-all
|
name: codeql/actions-all
|
||||||
version: 0.4.38
|
version: 0.4.39-dev
|
||||||
library: true
|
library: true
|
||||||
warnOnImplicitThis: true
|
warnOnImplicitThis: true
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/actions-queries
|
name: codeql/actions-queries
|
||||||
version: 0.6.30
|
version: 0.6.31-dev
|
||||||
library: false
|
library: false
|
||||||
warnOnImplicitThis: true
|
warnOnImplicitThis: true
|
||||||
groups: [actions, queries]
|
groups: [actions, queries]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/cpp-all
|
name: codeql/cpp-all
|
||||||
version: 11.0.0
|
version: 11.0.1-dev
|
||||||
groups: cpp
|
groups: cpp
|
||||||
dbscheme: semmlecode.cpp.dbscheme
|
dbscheme: semmlecode.cpp.dbscheme
|
||||||
extractor: cpp
|
extractor: cpp
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/cpp-queries
|
name: codeql/cpp-queries
|
||||||
version: 1.6.5
|
version: 1.6.6-dev
|
||||||
groups:
|
groups:
|
||||||
- cpp
|
- cpp
|
||||||
- queries
|
- queries
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/csharp-solorigate-all
|
name: codeql/csharp-solorigate-all
|
||||||
version: 1.7.69
|
version: 1.7.70-dev
|
||||||
groups:
|
groups:
|
||||||
- csharp
|
- csharp
|
||||||
- solorigate
|
- solorigate
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/csharp-solorigate-queries
|
name: codeql/csharp-solorigate-queries
|
||||||
version: 1.7.69
|
version: 1.7.70-dev
|
||||||
groups:
|
groups:
|
||||||
- csharp
|
- csharp
|
||||||
- solorigate
|
- solorigate
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/csharp-all
|
name: codeql/csharp-all
|
||||||
version: 7.0.0
|
version: 7.0.1-dev
|
||||||
groups: csharp
|
groups: csharp
|
||||||
dbscheme: semmlecode.csharp.dbscheme
|
dbscheme: semmlecode.csharp.dbscheme
|
||||||
extractor: csharp
|
extractor: csharp
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/csharp-queries
|
name: codeql/csharp-queries
|
||||||
version: 1.7.5
|
version: 1.7.6-dev
|
||||||
groups:
|
groups:
|
||||||
- csharp
|
- csharp
|
||||||
- queries
|
- queries
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ toolchain go1.26.4
|
|||||||
// bazel mod tidy
|
// bazel mod tidy
|
||||||
require (
|
require (
|
||||||
golang.org/x/mod v0.37.0
|
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
|
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/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 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
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.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
|
||||||
golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys=
|
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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql-go-consistency-queries
|
name: codeql-go-consistency-queries
|
||||||
version: 1.0.52
|
version: 1.0.53-dev
|
||||||
groups:
|
groups:
|
||||||
- go
|
- go
|
||||||
- queries
|
- queries
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/go-all
|
name: codeql/go-all
|
||||||
version: 7.2.0
|
version: 7.2.1-dev
|
||||||
groups: go
|
groups: go
|
||||||
dbscheme: go.dbscheme
|
dbscheme: go.dbscheme
|
||||||
extractor: go
|
extractor: go
|
||||||
|
|||||||
@@ -33,9 +33,11 @@ module StoredXss {
|
|||||||
walkFn.getACall().getArgument(1) = f.getASuccessor*()
|
walkFn.getACall().getArgument(1) = f.getASuccessor*()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// A call to os.FileInfo.Name
|
// The return value of a call to `os.DirEntry.Name`, `os.FileInfo.Name`
|
||||||
exists(Method m | m.implements("io/fs", "FileInfo", "Name") |
|
// or `os.File.ReadDirNames`.
|
||||||
m = this.(DataFlow::CallNode).getTarget()
|
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
|
name: codeql/go-queries
|
||||||
version: 1.6.5
|
version: 1.6.6-dev
|
||||||
groups:
|
groups:
|
||||||
- go
|
- go
|
||||||
- queries
|
- queries
|
||||||
|
|||||||
@@ -156,12 +156,3 @@ nodes
|
|||||||
| websocketXss.go:54:3:54:38 | ... := ...[1] | semmle.label | ... := ...[1] |
|
| websocketXss.go:54:3:54:38 | ... := ...[1] | semmle.label | ... := ...[1] |
|
||||||
| websocketXss.go:55:24:55:31 | gorilla3 | semmle.label | gorilla3 |
|
| websocketXss.go:55:24:55:31 | gorilla3 | semmle.label | gorilla3 |
|
||||||
subpaths
|
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
|
#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: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 |
|
| 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
|
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: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: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 | |
|
| stored.go:25:29:25:33 | &... [postupdate] | stored.go:30:22:30:25 | name | provenance | |
|
||||||
@@ -9,6 +11,8 @@ edges
|
|||||||
models
|
models
|
||||||
| 1 | Source: database/sql; DB; true; Query; ; ; ReturnValue[0]; database; manual |
|
| 1 | Source: database/sql; DB; true; Query; ; ; ReturnValue[0]; database; manual |
|
||||||
nodes
|
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:18:3:18:28 | ... := ...[0] | semmle.label | ... := ...[0] |
|
||||||
| stored.go:25:14:25:17 | rows | semmle.label | rows |
|
| stored.go:25:14:25:17 | rows | semmle.label | rows |
|
||||||
| stored.go:25:29:25:33 | &... [postupdate] | semmle.label | &... [postupdate] |
|
| 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:59:30:59:33 | SSA def(path) | semmle.label | SSA def(path) |
|
||||||
| stored.go:61:22:61:25 | path | semmle.label | path |
|
| stored.go:61:22:61:25 | path | semmle.label | path |
|
||||||
subpaths
|
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"
|
origin := "test"
|
||||||
{
|
{
|
||||||
ws, _ := websocket.Dial(uri, "", origin)
|
ws, _ := websocket.Dial(uri, "", origin)
|
||||||
var xnet = make([]byte, 512) // $ Source[go/reflected-xss]
|
var xnet = make([]byte, 512)
|
||||||
ws.Read(xnet)
|
ws.Read(xnet) // $ Source[go/reflected-xss]
|
||||||
fmt.Fprintf(w, "%v", xnet) // $ Alert[go/reflected-xss]
|
fmt.Fprintf(w, "%v", xnet) // $ Alert[go/reflected-xss]
|
||||||
codec := &websocket.Codec{Marshal: marshal, Unmarshal: unmarshal}
|
codec := &websocket.Codec{Marshal: marshal, Unmarshal: unmarshal}
|
||||||
xnet2 := make([]byte, 512) // $ Source[go/reflected-xss]
|
xnet2 := make([]byte, 512)
|
||||||
codec.Receive(ws, xnet2)
|
codec.Receive(ws, xnet2) // $ Source[go/reflected-xss]
|
||||||
fmt.Fprintf(w, "%v", xnet2) // $ Alert[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{}
|
dialer := gorilla.Dialer{}
|
||||||
conn, _, _ := dialer.Dial(uri, nil)
|
conn, _, _ := dialer.Dial(uri, nil)
|
||||||
var gorillaMsg = make([]byte, 512) // $ Source[go/reflected-xss]
|
var gorillaMsg = make([]byte, 512)
|
||||||
gorilla.ReadJSON(conn, gorillaMsg)
|
gorilla.ReadJSON(conn, gorillaMsg) // $ Source[go/reflected-xss]
|
||||||
fmt.Fprintf(w, "%v", gorillaMsg) // $ Alert[go/reflected-xss]
|
fmt.Fprintf(w, "%v", gorillaMsg) // $ Alert[go/reflected-xss]
|
||||||
|
|
||||||
gorilla2 := make([]byte, 512) // $ Source[go/reflected-xss]
|
gorilla2 := make([]byte, 512)
|
||||||
conn.ReadJSON(gorilla2)
|
conn.ReadJSON(gorilla2) // $ Source[go/reflected-xss]
|
||||||
fmt.Fprintf(w, "%v", gorilla2) // $ Alert[go/reflected-xss]
|
fmt.Fprintf(w, "%v", gorilla2) // $ Alert[go/reflected-xss]
|
||||||
|
|
||||||
_, gorilla3, _ := conn.ReadMessage() // $ Source[go/reflected-xss]
|
_, gorilla3, _ := conn.ReadMessage() // $ Source[go/reflected-xss]
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ pluginManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
|||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rootProject.name = "Android Sample"
|
rootProject.name = "Android Sample"
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ pluginManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
|||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rootProject.name = "Android Sample"
|
rootProject.name = "Android Sample"
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ pluginManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
|||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rootProject.name = "Android Sample"
|
rootProject.name = "Android Sample"
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ pluginManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
|||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rootProject.name = "Android Sample"
|
rootProject.name = "Android Sample"
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ buildscript {
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
maven {
|
||||||
|
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,6 +41,8 @@ buildscript {
|
|||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
maven {
|
||||||
|
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ buildscript {
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
maven {
|
||||||
|
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,6 +41,8 @@ buildscript {
|
|||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
maven {
|
||||||
|
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ buildscript {
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,6 +41,8 @@ buildscript {
|
|||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ buildscript {
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
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
|
* dependencies used by all modules in your project, such as third-party plugins
|
||||||
* or libraries. However, you should configure module-specific dependencies in
|
* or libraries. However, you should configure module-specific dependencies in
|
||||||
* each module-level build.gradle file. For new projects, Android Studio
|
* 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).
|
* configure any dependencies (unless you select a template that requires some).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ pluginManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
|||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rootProject.name = "Android Sample"
|
rootProject.name = "Android Sample"
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
apply plugin: 'java-library'
|
apply plugin: 'java-library'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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://maven-central.storage-download.googleapis.com/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://maven-central.storage-download.googleapis.com/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://maven-central.storage-download.googleapis.com/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://maven-central.storage-download.googleapis.com/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/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
apply plugin: 'java-library'
|
apply plugin: 'java-library'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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://maven-central.storage-download.googleapis.com/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/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
|
// In this section you declare where to find the dependencies of your project
|
||||||
repositories {
|
repositories {
|
||||||
// Use 'jcenter' for resolving your dependencies.
|
maven {
|
||||||
// You can declare any Maven/Ivy/file repository here.
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
jcenter()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this section you declare the dependencies for your production and test code
|
// In this section you declare the dependencies for your production and test code
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
apply plugin: 'java-library'
|
apply plugin: 'java-library'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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'
|
apply plugin: 'java-library'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.11/junit-4.11.jar
|
||||||
https://jcenter.bintray.com/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar
|
https://maven-central.storage-download.googleapis.com/maven2/junit/junit/4.12/junit-4.12.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/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/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/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
|
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/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/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/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/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-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
|
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
|
// In this section you declare where to find the dependencies of your project
|
||||||
repositories {
|
repositories {
|
||||||
// Use 'jcenter' for resolving your dependencies.
|
maven {
|
||||||
// You can declare any Maven/Ivy/file repository here.
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
jcenter()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this section you declare the dependencies for your production and test code
|
// 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
|
// In this section you declare where to find the dependencies of your project
|
||||||
repositories {
|
repositories {
|
||||||
// Use 'jcenter' for resolving your dependencies.
|
maven {
|
||||||
// You can declare any Maven/Ivy/file repository here.
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
jcenter()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this section you declare the dependencies for your production and test code
|
// 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/page.xml
|
||||||
maven-project-2/src/main/resources/struts.xml
|
maven-project-2/src/main/resources/struts.xml
|
||||||
maven-project-2/src/test/java/com/example/AppTest4.java
|
maven-project-2/src/test/java/com/example/AppTest4.java
|
||||||
|
settings.xml
|
||||||
test-db/working/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):
|
def test(codeql, use_java_11, java, actions_toolchains_file, check_diagnostics_java):
|
||||||
# The version of gradle used doesn't work on java 17
|
# The version of gradle used doesn't work on java 17
|
||||||
codeql.database.create(
|
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": "true",
|
||||||
"CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_CLASSPATH_FROM_BUILD_FILES": "true",
|
"CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_CLASSPATH_FROM_BUILD_FILES": "true",
|
||||||
"LGTM_INDEX_MAVEN_TOOLCHAINS_FILE": str(actions_toolchains_file),
|
"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 {
|
repositories {
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
@@ -33,7 +35,9 @@ dependencyResolutionManagement {
|
|||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rootProject.name = "Android Sample"
|
rootProject.name = "Android Sample"
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ apply plugin: 'java'
|
|||||||
|
|
||||||
// In this section you declare where to find the dependencies of your project
|
// In this section you declare where to find the dependencies of your project
|
||||||
repositories {
|
repositories {
|
||||||
// Use 'jcenter' for resolving your dependencies.
|
maven {
|
||||||
// You can declare any Maven/Ivy/file repository here.
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
jcenter()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this section you declare the dependencies for your production and test code
|
// 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
|
// In this section you declare where to find the dependencies of your project
|
||||||
repositories {
|
repositories {
|
||||||
// Use 'jcenter' for resolving your dependencies.
|
maven {
|
||||||
// You can declare any Maven/Ivy/file repository here.
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
jcenter()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this section you declare the dependencies for your production and test code
|
// In this section you declare the dependencies for your production and test code
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
// Use Maven Central for resolving dependencies.
|
maven {
|
||||||
mavenCentral()
|
url = uri("https://maven-central.storage-download.googleapis.com/maven2/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ apply plugin: 'java'
|
|||||||
|
|
||||||
// In this section you declare where to find the dependencies of your project
|
// In this section you declare where to find the dependencies of your project
|
||||||
repositories {
|
repositories {
|
||||||
// Use 'jcenter' for resolving your dependencies.
|
maven {
|
||||||
// You can declare any Maven/Ivy/file repository here.
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
jcenter()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this section you declare the dependencies for your production and test code
|
// 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
|
// In this section you declare where to find the dependencies of your project
|
||||||
repositories {
|
repositories {
|
||||||
// Use 'jcenter' for resolving your dependencies.
|
maven {
|
||||||
// You can declare any Maven/Ivy/file repository here.
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
jcenter()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this section you declare the dependencies for your production and test code
|
// 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
|
// In this section you declare where to find the dependencies of your project
|
||||||
repositories {
|
repositories {
|
||||||
// Use 'jcenter' for resolving your dependencies.
|
maven {
|
||||||
// You can declare any Maven/Ivy/file repository here.
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
jcenter()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this section you declare the dependencies for your production and test code
|
// 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
|
// In this section you declare where to find the dependencies of your project
|
||||||
repositories {
|
repositories {
|
||||||
// Use 'jcenter' for resolving your dependencies.
|
maven {
|
||||||
// You can declare any Maven/Ivy/file repository here.
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
jcenter()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this section you declare the dependencies for your production and test code
|
// 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.
|
// but I omit it to test we recognise the Spring Boot plugin version.
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
// Use Maven Central for resolving dependencies.
|
maven {
|
||||||
mavenCentral()
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
// Use Maven Central for resolving dependencies.
|
maven {
|
||||||
mavenCentral()
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
maven {
|
||||||
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
// Use Maven Central for resolving dependencies.
|
maven {
|
||||||
mavenCentral()
|
url = 'https://maven-central.storage-download.googleapis.com/maven2/'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/java-all
|
name: codeql/java-all
|
||||||
version: 9.2.0
|
version: 9.2.1-dev
|
||||||
groups: java
|
groups: java
|
||||||
dbscheme: config/semmlecode.dbscheme
|
dbscheme: config/semmlecode.dbscheme
|
||||||
extractor: java
|
extractor: java
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/java-queries
|
name: codeql/java-queries
|
||||||
version: 1.11.5
|
version: 1.11.6-dev
|
||||||
groups:
|
groups:
|
||||||
- java
|
- java
|
||||||
- queries
|
- 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
|
name: codeql/javascript-all
|
||||||
version: 2.8.0
|
version: 2.8.1-dev
|
||||||
groups: javascript
|
groups: javascript
|
||||||
dbscheme: semmlecode.javascript.dbscheme
|
dbscheme: semmlecode.javascript.dbscheme
|
||||||
extractor: javascript
|
extractor: javascript
|
||||||
|
|||||||
@@ -195,6 +195,18 @@ class PostMessageEventHandler extends Function {
|
|||||||
rhs = DataFlow::globalObjectRef().getAPropertyWrite("onmessage").getRhs() and
|
rhs = DataFlow::globalObjectRef().getAPropertyWrite("onmessage").getRhs() and
|
||||||
rhs.getABoundFunctionValue(paramIndex).getFunction() = this
|
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
|
name: codeql/javascript-queries
|
||||||
version: 2.4.0
|
version: 2.4.1-dev
|
||||||
groups:
|
groups:
|
||||||
- javascript
|
- javascript
|
||||||
- queries
|
- 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: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:24:27:24:27 | e | Postmessage handler has no origin check. |
|
||||||
| tst.js:40:27:40: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
|
name: codeql/suite-helpers
|
||||||
version: 1.0.52
|
version: 1.0.53-dev
|
||||||
groups: shared
|
groups: shared
|
||||||
warnOnImplicitThis: true
|
warnOnImplicitThis: true
|
||||||
|
|||||||
2
python/ql/consistency-queries/CfgConsistency.ql
Normal file
2
python/ql/consistency-queries/CfgConsistency.ql
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import semmle.python.controlflow.internal.AstNodeImpl
|
||||||
|
import ControlFlow::Consistency
|
||||||
4
python/ql/lib/change-notes/2026-05-19-add-shared-cfg.md
Normal file
4
python/ql/lib/change-notes/2026-05-19-add-shared-cfg.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* A new Python control flow graph implementation has been added under `semmle.python.controlflow.internal.Cfg` (backed by `AstNodeImpl.qll`), built on the shared `codeql.controlflow.ControlFlowGraph` library. It is not yet used by the dataflow library or any production query; the legacy CFG in `semmle/python/Flow.qll` remains the default. The new library is exposed for tests and for upcoming migrations.
|
||||||
4
python/ql/lib/change-notes/2026-05-19-add-shared-ssa.md
Normal file
4
python/ql/lib/change-notes/2026-05-19-add-shared-ssa.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* A new SSA adapter has been added under `semmle.python.dataflow.new.internal.SsaImpl`, built on the shared `codeql.ssa.Ssa` library and the new shared CFG (`semmle.python.controlflow.internal.Cfg`). It is not yet used by the dataflow library or any production query; the legacy ESSA SSA in `semmle/python/essa/*` remains the default. The new SSA adapter is exposed for tests and for the upcoming dataflow migration.
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* The new (shared-CFG-based) Python control flow graph now visits parameter and return type annotations as CFG nodes for function definitions, matching the legacy CFG. This restores annotation-based type tracking through framework models such as FastAPI's `Depends()`, Pydantic request models, Starlette `WebSocket` handlers, and any other models that flow a class reference through `Parameter.getAnnotation()` to identify instances of the annotated class.
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
---
|
|
||||||
category: minorAnalysis
|
|
||||||
---
|
|
||||||
* Type tracking of values stored in instance attributes and read from outside the class (for example `instance.attr` where the value was assigned to `self.attr` in a method) no longer relies on a dedicated instance type-tracker. This avoids a structural mutual recursion that could cause catastrophic query slowdowns on some OOP-heavy code bases. Such reads are now resolved using local flow from the constructor call, which is slightly less precise for instances that flow across a call or return before being read.
|
|
||||||
42
python/ql/lib/ide-contextual-queries/printCfg.ql
Normal file
42
python/ql/lib/ide-contextual-queries/printCfg.ql
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* @name Print CFG
|
||||||
|
* @description Produces a representation of a file's Control Flow Graph.
|
||||||
|
* This query is used by the VS Code extension.
|
||||||
|
* @id py/print-cfg
|
||||||
|
* @kind graph
|
||||||
|
* @tags ide-contextual-queries/print-cfg
|
||||||
|
*/
|
||||||
|
|
||||||
|
import semmle.python.Files as Files
|
||||||
|
// import semmle.python.Scope
|
||||||
|
import semmle.python.controlflow.internal.AstNodeImpl
|
||||||
|
|
||||||
|
external string selectedSourceFile();
|
||||||
|
|
||||||
|
private predicate selectedSourceFileAlias = selectedSourceFile/0;
|
||||||
|
|
||||||
|
external int selectedSourceLine();
|
||||||
|
|
||||||
|
private predicate selectedSourceLineAlias = selectedSourceLine/0;
|
||||||
|
|
||||||
|
external int selectedSourceColumn();
|
||||||
|
|
||||||
|
private predicate selectedSourceColumnAlias = selectedSourceColumn/0;
|
||||||
|
|
||||||
|
module ViewCfgQueryInput implements ControlFlow::ViewCfgQueryInputSig<Files::File> {
|
||||||
|
predicate selectedSourceFile = selectedSourceFileAlias/0;
|
||||||
|
|
||||||
|
predicate selectedSourceLine = selectedSourceLineAlias/0;
|
||||||
|
|
||||||
|
predicate selectedSourceColumn = selectedSourceColumnAlias/0;
|
||||||
|
|
||||||
|
predicate cfgScopeSpan(
|
||||||
|
Ast::Callable scope, Files::File file, int startLine, int startColumn, int endLine,
|
||||||
|
int endColumn
|
||||||
|
) {
|
||||||
|
file = scope.getLocation().getFile() and
|
||||||
|
scope.getLocation().hasLocationInfo(_, startLine, startColumn, endLine, endColumn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import ControlFlow::ViewCfgQuery<Files::File, ViewCfgQueryInput>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/python-all
|
name: codeql/python-all
|
||||||
version: 7.2.0
|
version: 7.2.1-dev
|
||||||
groups: python
|
groups: python
|
||||||
dbscheme: semmlecode.python.dbscheme
|
dbscheme: semmlecode.python.dbscheme
|
||||||
extractor: python
|
extractor: python
|
||||||
|
|||||||
1771
python/ql/lib/semmle/python/controlflow/internal/AstNodeImpl.qll
Normal file
1771
python/ql/lib/semmle/python/controlflow/internal/AstNodeImpl.qll
Normal file
File diff suppressed because it is too large
Load Diff
1025
python/ql/lib/semmle/python/controlflow/internal/Cfg.qll
Normal file
1025
python/ql/lib/semmle/python/controlflow/internal/Cfg.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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`
|
* Holds if the value that is being tracked is expected to be stored inside content `c`
|
||||||
* at node `n`.
|
* at node `n`.
|
||||||
*/
|
*/
|
||||||
predicate expectsContent(Node n, ContentSet c) {
|
predicate expectsContent(Node n, ContentSet c) { none() }
|
||||||
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if values stored inside attribute `c` are cleared at node `n`.
|
* 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 = ""
|
cs.isAnyTupleOrDictionaryElement() and result = "AnyTupleOrDictionaryElement" and arg = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
string encodeWithContent(ContentSet c, string arg) { result = "With" + encodeContent(c, arg) }
|
|
||||||
|
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) {
|
ParameterPosition decodeUnknownParameterPosition(AccessPath::AccessPathTokenBase token) {
|
||||||
// needed to support `Argument[x..y]` ranges
|
// needed to support `Argument[x..y]` ranges
|
||||||
|
|||||||
547
python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll
Normal file
547
python/ql/lib/semmle/python/dataflow/new/internal/SsaImpl.qll
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
/**
|
||||||
|
* Provides the Python SSA implementation built on the new (shared) CFG.
|
||||||
|
*
|
||||||
|
* Mirrors the Java SSA adapter at
|
||||||
|
* `java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll`:
|
||||||
|
* an `InputSig` is defined in terms of positional `(BasicBlock, int)`
|
||||||
|
* variable references, and the shared
|
||||||
|
* `codeql.ssa.Ssa::Make<Location, Cfg, Input>` module is then
|
||||||
|
* instantiated.
|
||||||
|
*
|
||||||
|
* `SourceVariable` is the AST-level `Py::Variable`. Variable references
|
||||||
|
* are looked up via the CFG facade's `NameNode.defines`/`uses`/`deletes`
|
||||||
|
* predicates, which themselves are one-line bridges to AST-level
|
||||||
|
* `Name.defines`/`uses`/`deletes`.
|
||||||
|
*
|
||||||
|
* Implicit-entry definitions are inserted for:
|
||||||
|
* - non-local / global / builtin variables that are read in the scope
|
||||||
|
* but never assigned (no enclosing CFG node defines them),
|
||||||
|
* - captured variables (variables defined in an enclosing scope that
|
||||||
|
* are read inside the scope), and
|
||||||
|
* - parameters, but only if the corresponding parameter name is *not*
|
||||||
|
* itself a CFG node. With the C#-style parameter wiring already
|
||||||
|
* installed in `AstNodeImpl.qll`, parameter names *are* CFG nodes,
|
||||||
|
* so the regular `variableWrite` path handles them — no `i = -1`
|
||||||
|
* entry is needed for ordinary parameters.
|
||||||
|
*/
|
||||||
|
overlay[local?]
|
||||||
|
module;
|
||||||
|
|
||||||
|
private import python as Py
|
||||||
|
private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
|
||||||
|
private import semmle.python.controlflow.internal.Cfg as Cfg
|
||||||
|
private import codeql.ssa.Ssa as SsaImplCommon
|
||||||
|
private import codeql.controlflow.BasicBlock as BB
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapts the Python `Cfg` facade to the shared SSA library's `CfgSig`.
|
||||||
|
* All members are inherited from `Cfg::ControlFlowNode` and
|
||||||
|
* `Cfg::BasicBlock`.
|
||||||
|
*/
|
||||||
|
private module CfgForSsa implements BB::CfgSig<Py::Location> {
|
||||||
|
class ControlFlowNode = CfgImpl::ControlFlowNode;
|
||||||
|
|
||||||
|
class BasicBlock = CfgImpl::BasicBlock;
|
||||||
|
|
||||||
|
class EntryBasicBlock = CfgImpl::Cfg::EntryBasicBlock;
|
||||||
|
|
||||||
|
predicate dominatingEdge = CfgImpl::Cfg::dominatingEdge/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A source variable for SSA, wrapping a Python AST `Variable`.
|
||||||
|
*
|
||||||
|
* We only track variables that are read at least once in their scope —
|
||||||
|
* tracking write-only variables would be unnecessary work — *except*
|
||||||
|
* for module-scope globals, where the "read" can be external (e.g.
|
||||||
|
* `import mymodule; mymodule.x`). Such globals are tracked
|
||||||
|
* unconditionally so that import-resolution can find their defining
|
||||||
|
* write.
|
||||||
|
*/
|
||||||
|
private newtype TSsaSourceVariable =
|
||||||
|
TPyVar(Py::Variable v) {
|
||||||
|
// Has a use somewhere — read-relevant for SSA.
|
||||||
|
exists(Cfg::NameNode n | n.uses(v))
|
||||||
|
or
|
||||||
|
// Or has a deletion (treated as a write that destroys the value).
|
||||||
|
exists(Cfg::NameNode n | n.deletes(v))
|
||||||
|
or
|
||||||
|
// Or is a module-scope global written in this module — must be
|
||||||
|
// tracked even if never read locally, because importers may read
|
||||||
|
// it as an attribute on the module object.
|
||||||
|
v.getScope() instanceof Py::Module and
|
||||||
|
exists(Cfg::NameNode n | n.defines(v))
|
||||||
|
or
|
||||||
|
// Or is a parameter — parameters must always have a
|
||||||
|
// `ParameterDefinition` for dataflow argument-routing to work,
|
||||||
|
// even if the parameter is never read in its scope. Mirrors
|
||||||
|
// legacy ESSA's `ParameterDefinition` (which fired for every
|
||||||
|
// parameter binding regardless of liveness).
|
||||||
|
exists(Py::Parameter p | p.asName() = v.getAStore())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A source variable for SSA, wrapping a Python AST `Variable`.
|
||||||
|
*/
|
||||||
|
class SsaSourceVariable extends TSsaSourceVariable {
|
||||||
|
/** Gets the underlying Python AST variable. */
|
||||||
|
Py::Variable getVariable() { this = TPyVar(result) }
|
||||||
|
|
||||||
|
/** Gets the (textual) name of this variable. */
|
||||||
|
string getName() { result = this.getVariable().getId() }
|
||||||
|
|
||||||
|
/** Gets a textual representation of this source variable. */
|
||||||
|
string toString() { result = this.getVariable().toString() }
|
||||||
|
|
||||||
|
/** Gets the location of this source variable. */
|
||||||
|
Py::Location getLocation() { result = this.getVariable().getScope().getLocation() }
|
||||||
|
|
||||||
|
/** Gets the scope in which this variable lives. */
|
||||||
|
Py::Scope getScope() { result = this.getVariable().getScope() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a use of this variable as it appears in the source — a `NameNode`
|
||||||
|
* that loads or deletes the variable. Mirrors legacy
|
||||||
|
* `SsaSourceVariable.getASourceUse()`.
|
||||||
|
*/
|
||||||
|
Cfg::ControlFlowNode getASourceUse() {
|
||||||
|
exists(Cfg::NameNode n | result = n |
|
||||||
|
n.uses(this.getVariable()) or n.deletes(this.getVariable())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an implicit use of this variable. The new SSA does not have
|
||||||
|
* implicit-use refinements, but we keep this for API parity — every
|
||||||
|
* normal-exit of the variable's scope counts as a sink, ensuring
|
||||||
|
* variables stay live to scope exit for taint-tracking.
|
||||||
|
*/
|
||||||
|
Cfg::ControlFlowNode getAnImplicitUse() {
|
||||||
|
result.isNormalExit() and result.getScope() = this.getScope()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a use of this variable — either an explicit source use or an
|
||||||
|
* implicit use at scope exit. Mirrors legacy `SsaSourceVariable.getAUse()`.
|
||||||
|
*/
|
||||||
|
Cfg::ControlFlowNode getAUse() {
|
||||||
|
result = this.getASourceUse() or result = this.getAnImplicitUse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `v` is a non-local read in scope `s`, in the sense that `s`
|
||||||
|
* uses `v` but does not write it within `s`. This includes globals,
|
||||||
|
* builtins, and variables captured from an enclosing function scope.
|
||||||
|
*
|
||||||
|
* The `Py::Variable` `v` lives in some defining scope (the module for
|
||||||
|
* globals, an outer function for closures, etc.); the reading scope
|
||||||
|
* `s` is the scope where the use of `v` occurs.
|
||||||
|
*/
|
||||||
|
private predicate nonLocalReadIn(Py::Variable v, Py::Scope s) {
|
||||||
|
exists(Cfg::NameNode n |
|
||||||
|
n.uses(v) and
|
||||||
|
n.getScope() = s and
|
||||||
|
not exists(Cfg::NameNode def | def.defines(v) and def.getScope() = s)
|
||||||
|
) and
|
||||||
|
// Match legacy ESSA: only create entry defs for variables that have
|
||||||
|
// at least one defining store somewhere — otherwise the entry def
|
||||||
|
// represents "nothing reaches here", which is the default anyway and
|
||||||
|
// introduces no useful flow. (Legacy's `ModuleVariable` required a
|
||||||
|
// store; this is the closure-aware generalisation.)
|
||||||
|
exists(Cfg::NameNode store | store.defines(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `bb` is the entry basic block of a scope where `v` should
|
||||||
|
* have an implicit entry definition. This covers:
|
||||||
|
* - non-local / global / builtin variables read in `s`, and
|
||||||
|
* - captured variables (defined in an enclosing scope but read in `s`).
|
||||||
|
*
|
||||||
|
* Each reading scope gets its own entry def, so a closure variable can
|
||||||
|
* have multiple entry defs across all functions/methods that read it.
|
||||||
|
*
|
||||||
|
* Parameters are *not* included: their bound `Name` is itself a CFG
|
||||||
|
* node (per the C#-style parameter wiring), so `variableWrite` fires at
|
||||||
|
* the parameter's natural CFG index.
|
||||||
|
*/
|
||||||
|
private predicate hasEntryDefIn(SsaSourceVariable v, CfgImpl::BasicBlock bb) {
|
||||||
|
exists(Py::Scope s |
|
||||||
|
nonLocalReadIn(v.getVariable(), s) and
|
||||||
|
bb = entryBlock(s)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the entry basic block of scope `s`, where implicit entry
|
||||||
|
* definitions are placed (at synthetic index `-1`).
|
||||||
|
*/
|
||||||
|
private CfgImpl::BasicBlock entryBlock(Py::Scope s) {
|
||||||
|
exists(CfgImpl::ControlFlowNode entry |
|
||||||
|
entry instanceof CfgImpl::ControlFlow::EntryNode and
|
||||||
|
entry.getEnclosingCallable().asScope() = s and
|
||||||
|
result = entry.getBasicBlock()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SSA `InputSig` for Python. References are positional
|
||||||
|
* `(BasicBlock, int)` pairs into the new CFG.
|
||||||
|
*/
|
||||||
|
private module SsaImplInput implements SsaImplCommon::InputSig<Py::Location, CfgImpl::BasicBlock> {
|
||||||
|
class SourceVariable = SsaSourceVariable;
|
||||||
|
|
||||||
|
predicate variableWrite(CfgImpl::BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||||
|
// Explicit binding at a CFG node — includes assignments,
|
||||||
|
// parameter Names (wired in via the C# pattern), exception-handler
|
||||||
|
// `as`-bindings, import aliases, and match-pattern captures.
|
||||||
|
exists(Cfg::NameNode n |
|
||||||
|
bb.getNode(i) = n and
|
||||||
|
n.defines(v.getVariable()) and
|
||||||
|
certain = true
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// `del x` — removes the binding. Modelled as a certain write that
|
||||||
|
// makes any subsequent read invalid.
|
||||||
|
exists(Cfg::NameNode n |
|
||||||
|
bb.getNode(i) = n and
|
||||||
|
n.deletes(v.getVariable()) and
|
||||||
|
certain = true
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// Implicit entry definition for non-local / captured / global /
|
||||||
|
// builtin variables read in some scope. Each reading scope's entry
|
||||||
|
// block gets one such write, allowing closures: e.g. when `x` is a
|
||||||
|
// parameter of an outer function and read inside a nested
|
||||||
|
// function, both scopes get entry defs for `x`.
|
||||||
|
hasEntryDefIn(v, bb) and
|
||||||
|
i = -1 and
|
||||||
|
certain = true
|
||||||
|
or
|
||||||
|
// `from X import *` — possibly rebinds every name in the importing
|
||||||
|
// scope. Modelled as an uncertain write at the import-star's CFG
|
||||||
|
// position for every variable that lives in (or is referenced
|
||||||
|
// from) the same scope as the import-star. Mirrors legacy ESSA's
|
||||||
|
// `ImportStarRefinement` (see `essa/SsaDefinitions.qll`'s
|
||||||
|
// `import_star_refinement` predicate). The write is uncertain so
|
||||||
|
// that prior definitions of the variable remain available — the
|
||||||
|
// shared-SSA `SsaUncertainWrite` merges the new value with the
|
||||||
|
// immediately preceding definition.
|
||||||
|
exists(Cfg::ImportStarNode imp |
|
||||||
|
bb.getNode(i) = imp and
|
||||||
|
certain = false and
|
||||||
|
(
|
||||||
|
v.getVariable().getScope() = imp.getScope()
|
||||||
|
or
|
||||||
|
// Variable is defined in some other scope but referenced in
|
||||||
|
// the same scope as the import-star (matches legacy clause 2:
|
||||||
|
// `other.uses(v) and def.getScope() = other.getScope()`).
|
||||||
|
exists(Cfg::NameNode other |
|
||||||
|
other.uses(v.getVariable()) and
|
||||||
|
imp.getScope() = other.getScope()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate variableRead(CfgImpl::BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||||
|
// Explicit source use — a `Name` load or a `del x` of the variable.
|
||||||
|
exists(Cfg::NameNode n |
|
||||||
|
bb.getNode(i) = n and
|
||||||
|
n.uses(v.getVariable()) and
|
||||||
|
certain = true
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// Synthetic use at the normal exit of the variable's defining scope.
|
||||||
|
// This keeps every variable live to scope exit so that callers (e.g.
|
||||||
|
// `module_export` in ImportResolution.qll, or taint-tracking pass-through
|
||||||
|
// through unread locals) can ask "which definition reaches end of
|
||||||
|
// scope?". Mirrors legacy ESSA's `SsaSourceVariable.getAUse()` which
|
||||||
|
// included `getScope().getANormalExit()`.
|
||||||
|
exists(Cfg::ControlFlowNode exit |
|
||||||
|
exit.isNormalExit() and
|
||||||
|
exit.getScope() = v.getVariable().getScope() and
|
||||||
|
bb.getNode(i) = exit and
|
||||||
|
certain = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The shared SSA instantiation for Python.
|
||||||
|
*
|
||||||
|
* Members:
|
||||||
|
* - `Definition` — the union of explicit, uncertain, and phi definitions
|
||||||
|
* - `WriteDefinition`, `UncertainWriteDefinition`, `PhiNode`
|
||||||
|
* - the standard SSA predicates (`getAUse`, `getAnUltimateDefinition`, ...).
|
||||||
|
*/
|
||||||
|
module Ssa = SsaImplCommon::Make<Py::Location, CfgForSsa, SsaImplInput>;
|
||||||
|
|
||||||
|
final class Definition = Ssa::Definition;
|
||||||
|
|
||||||
|
final class WriteDefinition = Ssa::WriteDefinition;
|
||||||
|
|
||||||
|
final class UncertainWriteDefinition = Ssa::UncertainWriteDefinition;
|
||||||
|
|
||||||
|
final class PhiNode = Ssa::PhiNode;
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// ESSA-shaped adapter layer
|
||||||
|
//
|
||||||
|
// The dataflow library (`python/ql/lib/semmle/python/dataflow/new/`) and
|
||||||
|
// related modules (`ApiGraphs.qll`, etc.) consume the legacy ESSA API
|
||||||
|
// (`EssaVariable`, `EssaDefinition`, `AssignmentDefinition`,
|
||||||
|
// `ScopeEntryDefinition`, `ParameterDefinition`, `WithDefinition`,
|
||||||
|
// `PhiFunction`, plus the `AdjacentUses` module). To migrate them off
|
||||||
|
// the legacy CFG, we expose the same API surface on top of the
|
||||||
|
// shared SSA built above.
|
||||||
|
//
|
||||||
|
// This adapter is intentionally narrow: it covers only the predicates
|
||||||
|
// that new dataflow consumes. The richer legacy ESSA — refinement
|
||||||
|
// nodes, attribute refinements, edge refinements — stays available
|
||||||
|
// via `semmle.python.essa.Essa` for points-to / legacy code.
|
||||||
|
// ===========================================================================
|
||||||
|
/**
|
||||||
|
* Gets the CFG node at which a write definition's binding takes place.
|
||||||
|
*
|
||||||
|
* For ordinary writes (assignment, deletion, parameter) this is the
|
||||||
|
* canonical CFG node of the bound Name. For implicit entry definitions
|
||||||
|
* (synthesised at position `-1` of a scope's entry BB) this is the
|
||||||
|
* scope's entry node.
|
||||||
|
*/
|
||||||
|
private Cfg::ControlFlowNode writeDefNode(Ssa::WriteDefinition def) {
|
||||||
|
exists(CfgImpl::BasicBlock bb, int i | def.definesAt(_, bb, i) |
|
||||||
|
i >= 0 and result = bb.getNode(i)
|
||||||
|
or
|
||||||
|
i = -1 and result = bb.getNode(0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A write definition whose binding has a corresponding CFG node — i.e.
|
||||||
|
* everything that's not a phi node. Mirrors legacy ESSA's
|
||||||
|
* `EssaNodeDefinition`.
|
||||||
|
*/
|
||||||
|
class EssaNodeDefinition extends Ssa::WriteDefinition {
|
||||||
|
/** Gets the CFG node where this definition's binding takes place. */
|
||||||
|
Cfg::ControlFlowNode getDefiningNode() { result = writeDefNode(this) }
|
||||||
|
|
||||||
|
/** Gets the variable defined here (legacy name). */
|
||||||
|
SsaSourceVariable getVariable() { result = this.getSourceVariable() }
|
||||||
|
|
||||||
|
/** Gets the enclosing scope. */
|
||||||
|
Py::Scope getScope() {
|
||||||
|
exists(Cfg::ControlFlowNode n | n = this.getDefiningNode() | result = n.getScope())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this definition defines source variable `v` at CFG node
|
||||||
|
* `defNode`. Flatter form of `getSourceVariable()` +
|
||||||
|
* `getDefiningNode()`, matching legacy ESSA's `definedBy`.
|
||||||
|
*/
|
||||||
|
predicate definedBy(SsaSourceVariable v, Cfg::ControlFlowNode defNode) {
|
||||||
|
v = this.getSourceVariable() and defNode = this.getDefiningNode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An assignment definition: any binding where the value being assigned
|
||||||
|
* is statically known via `Cfg::DefinitionNode.getValue()`. Includes
|
||||||
|
* plain assignments, walrus, annotated assignments, augmented
|
||||||
|
* assignments, import aliases (`import x` / `from m import x [as y]`),
|
||||||
|
* `with ... as x`, and for-target bindings (where `getValue()` returns
|
||||||
|
* the iter expression's CFG node). Excludes parameter bindings —
|
||||||
|
* those are modelled by `ParameterDefinition`.
|
||||||
|
*/
|
||||||
|
class AssignmentDefinition extends EssaNodeDefinition {
|
||||||
|
AssignmentDefinition() {
|
||||||
|
exists(Cfg::NameNode n | n = this.getDefiningNode() |
|
||||||
|
exists(n.(Cfg::DefinitionNode).getValue()) and
|
||||||
|
not n.(Cfg::ControlFlowNode).isParameter()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the CFG node for the value being assigned, if statically known. */
|
||||||
|
Cfg::ControlFlowNode getValue() {
|
||||||
|
result = this.getDefiningNode().(Cfg::DefinitionNode).getValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A parameter definition — the binding of a parameter name in a
|
||||||
|
* function's scope.
|
||||||
|
*/
|
||||||
|
class ParameterDefinition extends EssaNodeDefinition {
|
||||||
|
ParameterDefinition() { this.getDefiningNode().isParameter() }
|
||||||
|
|
||||||
|
/** Gets the AST `Parameter` (a `Py::Name` in param context). */
|
||||||
|
Py::Name getParameter() { result = this.getDefiningNode().getNode() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A definition introduced by a `with ... as x:` clause.
|
||||||
|
*/
|
||||||
|
class WithDefinition extends EssaNodeDefinition {
|
||||||
|
WithDefinition() {
|
||||||
|
exists(Cfg::NameNode n, Py::With w |
|
||||||
|
n = this.getDefiningNode() and
|
||||||
|
w.getOptionalVars() = n.getNode()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An assignment where the LHS is a tuple/list and the RHS is unpacked:
|
||||||
|
* `a, b = (1, 2)` or `a, *rest = xs`. The SSA def lives at the inner
|
||||||
|
* `Name` CFG node, but for IterableUnpacking integration we expose
|
||||||
|
* the enclosing `StarredNode` as the `getDefiningNode()` for `*rest`
|
||||||
|
* patterns — mirroring legacy ESSA's `multi_assignment_definition`,
|
||||||
|
* which placed the def at the StarredNode CFG node.
|
||||||
|
*/
|
||||||
|
class MultiAssignmentDefinition extends EssaNodeDefinition {
|
||||||
|
MultiAssignmentDefinition() {
|
||||||
|
exists(Cfg::NameNode n | n = super.getDefiningNode() |
|
||||||
|
exists(Py::Assign a, Py::Expr lhs |
|
||||||
|
a.getATarget() = lhs and
|
||||||
|
(lhs instanceof Py::Tuple or lhs instanceof Py::List) and
|
||||||
|
lhs.getASubExpression+() = n.getNode()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// For-loop with tuple/list target: `for a, b in xs:` —
|
||||||
|
// tuple-unpacking semantics applies to the for-target.
|
||||||
|
exists(Py::For f, Py::Expr lhs |
|
||||||
|
f.getTarget() = lhs and
|
||||||
|
(lhs instanceof Py::Tuple or lhs instanceof Py::List) and
|
||||||
|
lhs.getASubExpression+() = n.getNode()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override Cfg::ControlFlowNode getDefiningNode() {
|
||||||
|
// Default: the underlying `Name` CFG node (where the SSA def lives).
|
||||||
|
not exists(Cfg::StarredNode s |
|
||||||
|
s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode()
|
||||||
|
) and
|
||||||
|
result = super.getDefiningNode()
|
||||||
|
or
|
||||||
|
// Exception: for `*rest`, expose the enclosing `Starred` CFG node
|
||||||
|
// so that `IterableUnpacking::iterableUnpackingStarredElementStoreStep`
|
||||||
|
// can attach the rest-list to it.
|
||||||
|
exists(Cfg::StarredNode s |
|
||||||
|
s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode()
|
||||||
|
|
|
||||||
|
result = s
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implicit entry definition for a non-local / captured / global /
|
||||||
|
* builtin variable read in a scope but not defined there.
|
||||||
|
*
|
||||||
|
* Inherits from `EssaNodeDefinition` and exposes the scope's entry node
|
||||||
|
* as its defining node (matching legacy ESSA semantics).
|
||||||
|
*/
|
||||||
|
class ScopeEntryDefinition extends EssaNodeDefinition {
|
||||||
|
ScopeEntryDefinition() {
|
||||||
|
exists(CfgImpl::BasicBlock bb |
|
||||||
|
this.definesAt(_, bb, -1) and
|
||||||
|
bb instanceof CfgImpl::Cfg::EntryBasicBlock
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the enclosing scope (the scope whose entry block this def is in). */
|
||||||
|
override Py::Scope getScope() {
|
||||||
|
exists(CfgImpl::BasicBlock bb |
|
||||||
|
this.definesAt(_, bb, -1) and
|
||||||
|
result = bb.getNode(0).(Cfg::ControlFlowNode).getScope()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A phi node (alias matching legacy naming). */
|
||||||
|
class PhiFunction extends PhiNode {
|
||||||
|
/**
|
||||||
|
* Gets an input to this phi function (a definition that flows into
|
||||||
|
* the phi from one of its predecessor blocks). Mirrors legacy
|
||||||
|
* ESSA's `PhiFunction.getAnInput()`.
|
||||||
|
*/
|
||||||
|
Ssa::Definition getAnInput() { Ssa::phiHasInputFromBlock(this, result, _) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base class for all ESSA definitions (legacy-shaped). */
|
||||||
|
class EssaDefinition = Ssa::Definition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An adapter representing a single SSA-defined "variable" — wrapping
|
||||||
|
* one `Ssa::Definition`. Mirrors legacy `EssaVariable` API.
|
||||||
|
*/
|
||||||
|
class EssaVariable extends Ssa::Definition {
|
||||||
|
/** Gets the underlying SSA definition (legacy name). */
|
||||||
|
Ssa::Definition getDefinition() { result = this }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a CFG node where this definition is used. Includes regular
|
||||||
|
* `Name` reads as well as the synthetic scope-exit "use" registered
|
||||||
|
* via `SsaImplInput::variableRead` — mirrors legacy ESSA's
|
||||||
|
* `EssaVariable.getAUse()` which inherited the synthetic exit-use
|
||||||
|
* from `SsaSourceVariable`.
|
||||||
|
*/
|
||||||
|
Cfg::ControlFlowNode getAUse() {
|
||||||
|
exists(CfgImpl::BasicBlock bb, int i |
|
||||||
|
Ssa::ssaDefReachesRead(this.getSourceVariable(), this, bb, i) and
|
||||||
|
bb.getNode(i) = result
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the (textual) name of the underlying variable. */
|
||||||
|
string getName() { result = this.getSourceVariable().getVariable().getId() }
|
||||||
|
|
||||||
|
/** Gets the scope in which this variable lives. */
|
||||||
|
Py::Scope getScope() { result = this.getSourceVariable().getVariable().getScope() }
|
||||||
|
|
||||||
|
/** Gets an ultimate non-phi ancestor of this definition. */
|
||||||
|
EssaVariable getAnUltimateDefinition() {
|
||||||
|
if this instanceof PhiNode
|
||||||
|
then
|
||||||
|
exists(Ssa::Definition input |
|
||||||
|
Ssa::phiHasInputFromBlock(this, input, _) and
|
||||||
|
result = input.(EssaVariable).getAnUltimateDefinition()
|
||||||
|
)
|
||||||
|
else result = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjacent use-use and def-use relations exposed by the shared SSA
|
||||||
|
* library. Provides the same interface as legacy
|
||||||
|
* `semmle.python.essa.SsaCompute::AdjacentUses`.
|
||||||
|
*/
|
||||||
|
module AdjacentUses {
|
||||||
|
/** Holds if `nodeFrom` and `nodeTo` are adjacent uses of the same SSA variable. */
|
||||||
|
predicate adjacentUseUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) {
|
||||||
|
exists(SsaSourceVariable v, CfgImpl::BasicBlock bb1, int i1, CfgImpl::BasicBlock bb2, int i2 |
|
||||||
|
Ssa::adjacentUseUse(bb1, i1, bb2, i2, v, _) and
|
||||||
|
nodeFrom = bb1.getNode(i1) and
|
||||||
|
nodeTo = bb2.getNode(i2)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if `use` is a first use of definition `def`. */
|
||||||
|
predicate firstUse(Ssa::Definition def, Cfg::NameNode use) {
|
||||||
|
exists(CfgImpl::BasicBlock bb, int i |
|
||||||
|
Ssa::firstUse(def, bb, i, _) and
|
||||||
|
use = bb.getNode(i)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `use` is any reachable use of definition `def`. Combines
|
||||||
|
* `firstUse` with transitive use-use adjacency.
|
||||||
|
*/
|
||||||
|
predicate useOfDef(Ssa::Definition def, Cfg::NameNode use) {
|
||||||
|
firstUse(def, use)
|
||||||
|
or
|
||||||
|
exists(Cfg::NameNode mid | useOfDef(def, mid) and adjacentUseUse(mid, use))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -349,23 +349,11 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
|||||||
* `instance.attr`, where `instance` is a reference to an instance of `cls`).
|
* `instance.attr`, where `instance` is a reference to an instance of `cls`).
|
||||||
*
|
*
|
||||||
* This complements `selfAttrRef`, which only handles `self.attr` accesses inside the
|
* This complements `selfAttrRef`, which only handles `self.attr` accesses inside the
|
||||||
* methods of `cls`. The instance is identified using *local* flow from a constructor
|
* methods of `cls`. Unlike `selfAttrRef`, this depends on the call graph (via
|
||||||
* call `cls(...)` (resolved via the call graph by `resolveClassCall`), rather than a
|
* `classInstanceTracker`), so steps using it must be reported as `levelStepCall`.
|
||||||
* dedicated instance type-tracker (`classInstanceTracker`).
|
|
||||||
*
|
|
||||||
* Using `classInstanceTracker` here would make `levelStepCall` mutually recursive with
|
|
||||||
* `classInstanceTracker` -- itself a full type-tracker run -- which caused catastrophic
|
|
||||||
* query slowdowns on some OOP-heavy Python code bases (e.g. `mypy` and `dask`). Relying
|
|
||||||
* on local flow from a resolved constructor call instead depends only on `classTracker`
|
|
||||||
* (the same call-graph machinery already used by `inheritedFieldStep`), avoiding that
|
|
||||||
* blow-up. The trade-off is reduced precision: instances that flow across a call or
|
|
||||||
* return before being read are no longer covered by this step.
|
|
||||||
*/
|
*/
|
||||||
private predicate instanceAttrRead(Class cls, string attr, DataFlowPublic::AttrRead read) {
|
private predicate instanceAttrRead(Class cls, string attr, DataFlowPublic::AttrRead read) {
|
||||||
exists(DataFlowPublic::CallCfgNode construction |
|
read.getObject() = DataFlowDispatch::classInstanceTracker(cls) and
|
||||||
DataFlowDispatch::resolveClassCall(construction.asCfgNode(), cls) and
|
|
||||||
read.getObject().getALocalSource() = construction
|
|
||||||
) and
|
|
||||||
read.mayHaveAttributeName(attr)
|
read.mayHaveAttributeName(attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,9 +432,9 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
|||||||
* This is the cross-instance counterpart of `localFieldStep`: it relates a write of
|
* This is the cross-instance counterpart of `localFieldStep`: it relates a write of
|
||||||
* `self.attr` inside a class to a read of `attr` on a reference to an instance of that
|
* `self.attr` inside a class to a read of `attr` on a reference to an instance of that
|
||||||
* class or one of its subclasses. Identifying instances relies on the call graph (via
|
* class or one of its subclasses. Identifying instances relies on the call graph (via
|
||||||
* `resolveClassCall`, see `instanceAttrRead`), so this step is reported as
|
* `classInstanceTracker`), so this step is reported as `levelStepCall` rather than
|
||||||
* `levelStepCall` rather than `levelStepNoCall`. The write may occur in the instance's
|
* `levelStepNoCall`. The write may occur in the instance's own class or in any of its
|
||||||
* own class or in any of its superclasses, since those methods are inherited.
|
* superclasses, since those methods are inherited.
|
||||||
*
|
*
|
||||||
* Like `localFieldStep`, this is an over-approximation: it is both instance-insensitive
|
* Like `localFieldStep`, this is an over-approximation: it is both instance-insensitive
|
||||||
* and order-insensitive.
|
* and order-insensitive.
|
||||||
|
|||||||
@@ -71,14 +71,21 @@ module Flask {
|
|||||||
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.
|
* See https://flask.palletsprojects.com/en/1.1.x/api/#flask.Flask.
|
||||||
*/
|
*/
|
||||||
module FlaskApp {
|
module FlaskApp {
|
||||||
/** Gets a reference to the `flask.Flask` class. */
|
/**
|
||||||
API::Node classRef() {
|
* Gets a reference to the `flask.Flask` class or any subclass.
|
||||||
result = API::moduleImport("flask").getMember("Flask") or
|
*
|
||||||
|
* 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*()
|
result = ModelOutput::getATypeNode("flask.Flask~Subclass").getASubclass*()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets a reference to an instance of `flask.Flask` (a flask application). */
|
/** 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() {
|
API::Node classRef() {
|
||||||
result = API::moduleImport("flask").getMember("Response")
|
result = API::moduleImport("flask").getMember("Response")
|
||||||
or
|
or
|
||||||
result = [FlaskApp::classRef(), FlaskApp::instance()].getMember("response_class")
|
result = [FlaskApp::subclassRef(), FlaskApp::instance()].getMember("response_class")
|
||||||
or
|
or
|
||||||
result = ModelOutput::getATypeNode("flask.Response~Subclass").getASubclass*()
|
result = ModelOutput::getATypeNode("flask.Response~Subclass").getASubclass*()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4199,9 +4199,11 @@ module StdlibPrivate {
|
|||||||
// The positional argument contains a mapping.
|
// The positional argument contains a mapping.
|
||||||
// TODO: these values can be overwritten by keyword arguments
|
// TODO: these values can be overwritten by keyword arguments
|
||||||
// - dict mapping
|
// - dict mapping
|
||||||
input = "Argument[0].WithAnyDictionaryElement" and
|
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||||
output = "ReturnValue" and
|
input = "Argument[0].DictionaryElement[" + key + "]" and
|
||||||
|
output = "ReturnValue.DictionaryElement[" + key + "]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
)
|
||||||
or
|
or
|
||||||
// - list-of-pairs mapping
|
// - list-of-pairs mapping
|
||||||
input = "Argument[0].ListElement.TupleElement[1]" and
|
input = "Argument[0].ListElement.TupleElement[1]" and
|
||||||
@@ -4238,7 +4240,9 @@ module StdlibPrivate {
|
|||||||
or
|
or
|
||||||
input = "Argument[0].SetElement"
|
input = "Argument[0].SetElement"
|
||||||
or
|
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
|
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||||
) and
|
) and
|
||||||
// Element content is mutated into list element content
|
// Element content is mutated into list element content
|
||||||
@@ -4262,9 +4266,11 @@ module StdlibPrivate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0].WithAnyTupleElement" and
|
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||||
output = "ReturnValue" and
|
input = "Argument[0].TupleElement[" + i.toString() + "]" and
|
||||||
|
output = "ReturnValue.TupleElement[" + i.toString() + "]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
)
|
||||||
or
|
or
|
||||||
input = "Argument[0].ListElement" and
|
input = "Argument[0].ListElement" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
@@ -4288,7 +4294,9 @@ module StdlibPrivate {
|
|||||||
or
|
or
|
||||||
input = "Argument[0].SetElement"
|
input = "Argument[0].SetElement"
|
||||||
or
|
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
|
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||||
) and
|
) and
|
||||||
output = "ReturnValue.SetElement" and
|
output = "ReturnValue.SetElement" and
|
||||||
@@ -4334,7 +4342,9 @@ module StdlibPrivate {
|
|||||||
or
|
or
|
||||||
input = "Argument[0].SetElement"
|
input = "Argument[0].SetElement"
|
||||||
or
|
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
|
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||||
) and
|
) and
|
||||||
output = "ReturnValue.ListElement" and
|
output = "ReturnValue.ListElement" and
|
||||||
@@ -4362,7 +4372,9 @@ module StdlibPrivate {
|
|||||||
or
|
or
|
||||||
content = "SetElement"
|
content = "SetElement"
|
||||||
or
|
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
|
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||||
input = "Argument[0]." + content and
|
input = "Argument[0]." + content and
|
||||||
@@ -4392,7 +4404,9 @@ module StdlibPrivate {
|
|||||||
or
|
or
|
||||||
input = "Argument[0].SetElement"
|
input = "Argument[0].SetElement"
|
||||||
or
|
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
|
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||||
) and
|
) and
|
||||||
output = "ReturnValue.ListElement" and
|
output = "ReturnValue.ListElement" and
|
||||||
@@ -4420,7 +4434,9 @@ module StdlibPrivate {
|
|||||||
or
|
or
|
||||||
input = "Argument[0].SetElement"
|
input = "Argument[0].SetElement"
|
||||||
or
|
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
|
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||||
) and
|
) and
|
||||||
output = "ReturnValue" 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.
|
// 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.
|
// TODO: Once we have TupleElementAny, this generality can be increased.
|
||||||
i = 0 and
|
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
|
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||||
) and
|
) and
|
||||||
output = "Argument[0].Parameter[" + i.toString() + "]" and
|
output = "Argument[0].Parameter[" + i.toString() + "]" and
|
||||||
@@ -4481,7 +4499,9 @@ module StdlibPrivate {
|
|||||||
or
|
or
|
||||||
input = "Argument[1].SetElement"
|
input = "Argument[1].SetElement"
|
||||||
or
|
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
|
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||||
) and
|
) and
|
||||||
(output = "Argument[0].Parameter[0]" or output = "ReturnValue.ListElement") and
|
(output = "Argument[0].Parameter[0]" or output = "ReturnValue.ListElement") and
|
||||||
@@ -4505,7 +4525,9 @@ module StdlibPrivate {
|
|||||||
or
|
or
|
||||||
input = "Argument[0].SetElement"
|
input = "Argument[0].SetElement"
|
||||||
or
|
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
|
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||||
) and
|
) and
|
||||||
output = "ReturnValue.ListElement.TupleElement[1]" and
|
output = "ReturnValue.ListElement.TupleElement[1]" and
|
||||||
@@ -4530,7 +4552,12 @@ module StdlibPrivate {
|
|||||||
or
|
or
|
||||||
input = "Argument[" + i.toString() + "].SetElement"
|
input = "Argument[" + i.toString() + "].SetElement"
|
||||||
or
|
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
|
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||||
) and
|
) and
|
||||||
output = "ReturnValue.ListElement.TupleElement[" + i.toString() + "]" and
|
output = "ReturnValue.ListElement.TupleElement[" + i.toString() + "]" and
|
||||||
@@ -4553,6 +4580,12 @@ module StdlibPrivate {
|
|||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
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
|
input = "Argument[self]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
@@ -4708,10 +4741,12 @@ module StdlibPrivate {
|
|||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].AnyDictionaryElement" and
|
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||||
|
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||||
output = "ReturnValue.TupleElement[1]" and
|
output = "ReturnValue.TupleElement[1]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
// TODO: put `key` into "ReturnValue.TupleElement[0]"
|
// TODO: put `key` into "ReturnValue.TupleElement[0]"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4790,9 +4825,11 @@ module StdlibPrivate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].AnyDictionaryElement" and
|
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||||
|
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||||
output = "ReturnValue.ListElement" and
|
output = "ReturnValue.ListElement" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
)
|
||||||
or
|
or
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
@@ -4839,9 +4876,11 @@ module StdlibPrivate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].AnyDictionaryElement" and
|
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||||
|
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||||
output = "ReturnValue.ListElement.TupleElement[1]" and
|
output = "ReturnValue.ListElement.TupleElement[1]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
)
|
||||||
or
|
or
|
||||||
// TODO: Add the keys to output list
|
// TODO: Add the keys to output list
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ class DjangoHttpRequest extends FindSubclassesSpec {
|
|||||||
class FlaskClass extends FindSubclassesSpec {
|
class FlaskClass extends FindSubclassesSpec {
|
||||||
FlaskClass() { this = "flask.Flask~Subclass" }
|
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 {
|
class FlaskBlueprint extends FindSubclassesSpec {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: codeql/python-queries
|
name: codeql/python-queries
|
||||||
version: 1.8.5
|
version: 1.8.6-dev
|
||||||
groups:
|
groups:
|
||||||
- python
|
- python
|
||||||
- queries
|
- 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>
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
consistencyOverview
|
||||||
|
| deadEnd | 1 |
|
||||||
|
deadEnd
|
||||||
|
| without_loop.py:7:5:7:9 | Break |
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Phase -1 of the dataflow CFG migration: verifies that every variable
|
||||||
|
* binding visible to the AST (`Name.defines(v)`) corresponds to a CFG node
|
||||||
|
* in the new CFG (`semmle.python.controlflow.internal.AstNodeImpl`).
|
||||||
|
*
|
||||||
|
* The expected tag is `cfgdefines=<name>`. Each binding annotation in the
|
||||||
|
* test sources looks like `# $ cfgdefines=x` for a binding currently
|
||||||
|
* covered by the new CFG, or `# $ MISSING: cfgdefines=x` for a binding
|
||||||
|
* that is known to be uncovered (a "red" test case that should be
|
||||||
|
* green-flipped once the corresponding `cfg-ext-*` extension lands).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import python
|
||||||
|
import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
|
||||||
|
import utils.test.InlineExpectationsTest
|
||||||
|
|
||||||
|
module CfgBindingsTest implements TestSig {
|
||||||
|
string getARelevantTag() { result = "cfgdefines" }
|
||||||
|
|
||||||
|
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||||
|
exists(Name n, Variable v, CfgImpl::ControlFlowNode cfg |
|
||||||
|
n.defines(v) and
|
||||||
|
cfg.getAstNode().asExpr() = n and
|
||||||
|
location = n.getLocation() and
|
||||||
|
element = n.toString() and
|
||||||
|
tag = "cfgdefines" and
|
||||||
|
value = v.getId()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
import MakeTest<CfgBindingsTest>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Annotated assignment (PEP 526). Both with and without an initializer.
|
||||||
|
|
||||||
|
a: int = 1 # $ cfgdefines=a
|
||||||
|
b: str = "hi" # $ cfgdefines=b
|
||||||
|
|
||||||
|
# Annotation without value: the AST records `c` as defined,
|
||||||
|
# and the new CFG now visits it via the AnnAssignStmt wrapper.
|
||||||
|
c: int # $ cfgdefines=c
|
||||||
|
|
||||||
|
class K: # $ cfgdefines=K
|
||||||
|
field: int = 0 # $ cfgdefines=field
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Compound (tuple/list) assignment targets — actually wired in the new CFG.
|
||||||
|
|
||||||
|
a, b = (1, 2) # $ cfgdefines=a cfgdefines=b
|
||||||
|
[c, d] = [3, 4] # $ cfgdefines=c cfgdefines=d
|
||||||
|
|
||||||
|
# Nested unpacking.
|
||||||
|
(e, (f, g)) = (1, (2, 3)) # $ cfgdefines=e cfgdefines=f cfgdefines=g
|
||||||
|
|
||||||
|
# Star unpacking.
|
||||||
|
h, *i = [1, 2, 3] # $ cfgdefines=h cfgdefines=i
|
||||||
|
|
||||||
|
# Chained assignment with compound target.
|
||||||
|
j = k, l = (5, 6) # $ cfgdefines=j cfgdefines=k cfgdefines=l
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Comprehension and `for` loop targets — wired in the new CFG.
|
||||||
|
# Comprehensions are nested function scopes with a synthetic `.0` parameter
|
||||||
|
# bound to the iterable.
|
||||||
|
|
||||||
|
# Bare-name `for` target.
|
||||||
|
for i in range(3): # $ cfgdefines=i
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Compound `for` target.
|
||||||
|
for k, v in [(1, 2)]: # $ cfgdefines=k cfgdefines=v
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Comprehension targets.
|
||||||
|
_ = [x for x in range(3)] # $ cfgdefines=_ cfgdefines=x cfgdefines=.0
|
||||||
|
_ = {y: z for y, z in []} # $ cfgdefines=_ cfgdefines=y cfgdefines=z cfgdefines=.0
|
||||||
|
_ = (a for a in []) # $ cfgdefines=_ cfgdefines=a cfgdefines=.0
|
||||||
|
|
||||||
|
# Nested comprehensions.
|
||||||
|
_ = [b for c in [] for b in c] # $ cfgdefines=_ cfgdefines=c cfgdefines=b cfgdefines=.0
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
# Reachability of code following a try whose body always returns.
|
||||||
|
#
|
||||||
|
# The new CFG models exception edges for raise-prone expressions when
|
||||||
|
# they appear inside a `try` (or `with`) statement, mirroring Java's
|
||||||
|
# `mayThrow`. This means the body of a `try` has both a normal
|
||||||
|
# completion edge and an exception edge to its handlers, so code
|
||||||
|
# following the try-statement is reachable via the except-handler path
|
||||||
|
# even when the try-body would otherwise always return.
|
||||||
|
#
|
||||||
|
# Code that is not reachable under either normal or exception flow
|
||||||
|
# (for example, the `else` clause of a try whose body unconditionally
|
||||||
|
# raises) remains correctly classified as dead.
|
||||||
|
|
||||||
|
|
||||||
|
def f(obj): # $ cfgdefines=f cfgdefines=obj
|
||||||
|
try:
|
||||||
|
return len(obj)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# The try-body always returns, but `len(obj)` can raise (it is
|
||||||
|
# inside the try, so we model its exception edge). The
|
||||||
|
# `except TypeError: pass` handler falls through to here, making
|
||||||
|
# the code below reachable.
|
||||||
|
try:
|
||||||
|
hint = type(obj).__length_hint__ # $ cfgdefines=hint
|
||||||
|
except AttributeError:
|
||||||
|
return None
|
||||||
|
return hint
|
||||||
|
|
||||||
|
|
||||||
|
def g(): # $ cfgdefines=g
|
||||||
|
try:
|
||||||
|
raise Exception("inner")
|
||||||
|
except:
|
||||||
|
raise Exception("outer")
|
||||||
|
else:
|
||||||
|
# Unreachable: the inner try body always raises (via an explicit
|
||||||
|
# `raise`, which is modelled unconditionally), so the `else:`
|
||||||
|
# clause never runs.
|
||||||
|
hit_inner_else = True
|
||||||
|
|
||||||
|
|
||||||
|
def h(cache, key): # $ cfgdefines=h cfgdefines=cache cfgdefines=key
|
||||||
|
try:
|
||||||
|
return cache[key]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Same pattern as `f`: reachable via the except-handler fall-through.
|
||||||
|
value = compute(key) # $ cfgdefines=value
|
||||||
|
cache[key] = value
|
||||||
|
return value
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Decorated `def`/`class` — wired in the new CFG.
|
||||||
|
|
||||||
|
|
||||||
|
def deco(f): # $ cfgdefines=deco cfgdefines=f
|
||||||
|
return f
|
||||||
|
|
||||||
|
|
||||||
|
@deco
|
||||||
|
def decorated_func(): # $ cfgdefines=decorated_func
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@deco
|
||||||
|
class DecoratedClass: # $ cfgdefines=DecoratedClass
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Stacked decorators.
|
||||||
|
@deco
|
||||||
|
@deco
|
||||||
|
def doubly(): # $ cfgdefines=doubly
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Inside a class body.
|
||||||
|
class Outer: # $ cfgdefines=Outer
|
||||||
|
@staticmethod
|
||||||
|
def inner(): # $ cfgdefines=inner
|
||||||
|
pass
|
||||||
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Exception-handler name bindings. These are already wired in the new
|
||||||
|
# CFG provided the try body can raise; `raise` statements are reliably
|
||||||
|
# treated as exception sources.
|
||||||
|
|
||||||
|
try:
|
||||||
|
raise ValueError("oops")
|
||||||
|
except ValueError as e: # $ cfgdefines=e
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
raise TypeError("oops")
|
||||||
|
except (TypeError, KeyError) as err: # $ cfgdefines=err
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Exception groups (Python 3.11+).
|
||||||
|
try:
|
||||||
|
raise ValueError("oops")
|
||||||
|
except* ValueError as eg: # $ cfgdefines=eg
|
||||||
|
pass
|
||||||
14
python/ql/test/library-tests/ControlFlow/bindings/imports.py
Normal file
14
python/ql/test/library-tests/ControlFlow/bindings/imports.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Import aliases — all bound names below are now reachable via the new
|
||||||
|
# CFG's `ImportStmt` wrapper.
|
||||||
|
|
||||||
|
import os # $ cfgdefines=os
|
||||||
|
import os.path # $ cfgdefines=os
|
||||||
|
import os as o # $ cfgdefines=o
|
||||||
|
from os import path # $ cfgdefines=path
|
||||||
|
from os import path as p # $ cfgdefines=p
|
||||||
|
from os import sep, linesep # $ cfgdefines=sep cfgdefines=linesep
|
||||||
|
from os import (
|
||||||
|
getcwd, # $ cfgdefines=getcwd
|
||||||
|
getcwdb, # $ cfgdefines=getcwdb
|
||||||
|
)
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Match-statement pattern bindings — wired in the new CFG.
|
||||||
|
|
||||||
|
def f(subject): # $ cfgdefines=f cfgdefines=subject
|
||||||
|
match subject:
|
||||||
|
case x: # $ cfgdefines=x
|
||||||
|
pass
|
||||||
|
case [a, b]: # $ cfgdefines=a cfgdefines=b
|
||||||
|
pass
|
||||||
|
case {"k": v}: # $ cfgdefines=v
|
||||||
|
pass
|
||||||
|
case Point(p, q): # $ cfgdefines=p cfgdefines=q
|
||||||
|
pass
|
||||||
|
case [_, *rest]: # $ cfgdefines=rest
|
||||||
|
pass
|
||||||
|
case (1 | 2) as n: # $ cfgdefines=n
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Point: # $ cfgdefines=Point
|
||||||
|
__match_args__ = ("x", "y") # $ cfgdefines=__match_args__
|
||||||
|
x: int # $ cfgdefines=x
|
||||||
|
y: int # $ cfgdefines=y
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
# Function parameters.
|
||||||
|
|
||||||
|
def positional(a, b): # $ cfgdefines=positional cfgdefines=a cfgdefines=b
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def with_default(x=1, y=2): # $ cfgdefines=with_default cfgdefines=x cfgdefines=y
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def with_vararg(*args): # $ cfgdefines=with_vararg cfgdefines=args
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def with_kwarg(**kwargs): # $ cfgdefines=with_kwarg cfgdefines=kwargs
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def with_kwonly(*, k1, k2=5): # $ cfgdefines=with_kwonly cfgdefines=k1 cfgdefines=k2
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def kitchen_sink(a, b=2, *args, k1, k2=5, **kw): # $ cfgdefines=kitchen_sink cfgdefines=a cfgdefines=b cfgdefines=args cfgdefines=k1 cfgdefines=k2 cfgdefines=kw
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Methods get `self` / `cls`.
|
||||||
|
class C: # $ cfgdefines=C
|
||||||
|
def method(self, x): # $ cfgdefines=method cfgdefines=self cfgdefines=x
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def cmethod(cls, x): # $ cfgdefines=cmethod cfgdefines=cls cfgdefines=x
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Lambda parameter.
|
||||||
|
_ = lambda p: p + 1 # $ cfgdefines=_ cfgdefines=p
|
||||||
|
|
||||||
|
# PEP 570 positional-only.
|
||||||
|
def pos_only(a, b, /, c): # $ cfgdefines=pos_only cfgdefines=a cfgdefines=b cfgdefines=c
|
||||||
|
pass
|
||||||
14
python/ql/test/library-tests/ControlFlow/bindings/simple.py
Normal file
14
python/ql/test/library-tests/ControlFlow/bindings/simple.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Simple bindings that should already work in the new CFG.
|
||||||
|
# No MISSING annotations expected.
|
||||||
|
|
||||||
|
x = 1 # $ cfgdefines=x
|
||||||
|
y = x + 1 # $ cfgdefines=y
|
||||||
|
|
||||||
|
def f(): # $ cfgdefines=f
|
||||||
|
pass
|
||||||
|
|
||||||
|
class C: # $ cfgdefines=C
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Re-assignment.
|
||||||
|
x = 2 # $ cfgdefines=x
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# PEP 695 type parameters (Python 3.12+).
|
||||||
|
|
||||||
|
# PEP 695 type-param names on `def`/`class` bind in an annotation scope
|
||||||
|
# that nests the function/class body — they have no CFG node in the
|
||||||
|
# enclosing scope (matching the legacy CFG).
|
||||||
|
def func[T](x: T) -> T: # $ cfgdefines=func cfgdefines=x
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
class Box[T]: # $ cfgdefines=Box
|
||||||
|
item: T # $ cfgdefines=item
|
||||||
|
|
||||||
|
|
||||||
|
# Multi-parameter, with bound and variadics.
|
||||||
|
def multi[T: int, *Ts, **P](x: T, *args: *Ts, **kwargs: P.kwargs) -> T: # $ cfgdefines=multi cfgdefines=x cfgdefines=args cfgdefines=kwargs
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
# `type` statement (PEP 695).
|
||||||
|
type Alias[T] = list[T] # $ cfgdefines=Alias cfgdefines=T
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Walrus and starred-target edge cases — wired in the new CFG.
|
||||||
|
|
||||||
|
# Walrus in expression context.
|
||||||
|
if (y := 5) > 0: # $ cfgdefines=y
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Walrus in a comprehension. The comprehension introduces a synthetic
|
||||||
|
# `.0` parameter bound to the iterable.
|
||||||
|
_ = [w for _ in range(3) if (w := 1)] # $ cfgdefines=_ cfgdefines=w cfgdefines=.0
|
||||||
|
|
||||||
|
# Starred target in a Tuple LHS.
|
||||||
|
*head, tail = [1, 2, 3] # $ cfgdefines=head cfgdefines=tail
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# `with cm() as x:` bindings — wired in the new CFG.
|
||||||
|
|
||||||
|
class CM: # $ cfgdefines=CM
|
||||||
|
def __enter__(self): return self # $ cfgdefines=__enter__ cfgdefines=self
|
||||||
|
def __exit__(self, *a): pass # $ cfgdefines=__exit__ cfgdefines=self cfgdefines=a
|
||||||
|
|
||||||
|
with CM() as x: # $ cfgdefines=x
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Multiple items.
|
||||||
|
with CM() as a, CM() as b: # $ cfgdefines=a cfgdefines=b
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Parenthesised form (Python 3.10+).
|
||||||
|
with (CM() as p, CM() as q): # $ cfgdefines=p cfgdefines=q
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Compound target in `with`.
|
||||||
|
with CM() as (m, n): # $ cfgdefines=m cfgdefines=n
|
||||||
|
pass
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/** New-CFG version of AllLiveReachable. */
|
||||||
|
|
||||||
|
import python
|
||||||
|
import TimerUtils
|
||||||
|
import NewCfgImpl
|
||||||
|
|
||||||
|
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||||
|
|
||||||
|
private import Utils
|
||||||
|
private import Utils::CfgTests
|
||||||
|
|
||||||
|
from TimerCfgNode a, TestFunction f
|
||||||
|
where allLiveReachable(a, f)
|
||||||
|
select a, "Unreachable live annotation; entry of $@ does not reach this node", f, f.getName()
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user