wip: jedis.model.yml via model editor, and settings for it

This commit is contained in:
Michael Hohn
2025-07-08 17:38:06 -07:00
committed by =Michael Hohn
parent 514cdf9f06
commit 9f75a5c6f5
13 changed files with 606 additions and 11 deletions

2
.gitattributes vendored
View File

@@ -1 +1,3 @@
*.zip filter=lfs diff=lfs merge=lfs -text
*.jar filter=lfs diff=lfs merge=lfs -text

View File

@@ -0,0 +1,26 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: sourceModel
data: []
- addsTo:
pack: codeql/java-all
extensible: sinkModel
data:
- ["redis.clients.jedis","Jedis",true,"eval","(String)","","Argument[0]","code-injection","manual"]
- ["redis.clients.jedis","Jedis",true,"eval","(byte[],int,byte[][])","","Argument[0]","code-injection","manual"]
- ["redis.clients.jedis","Jedis",true,"eval","(byte[],List,List)","","Argument[0]","code-injection","manual"]
- ["redis.clients.jedis","Jedis",true,"eval","(byte[])","","Argument[0]","code-injection","manual"]
- ["redis.clients.jedis","Jedis",true,"eval","(String,int,String[])","","Argument[0]","code-injection","manual"]
- ["redis.clients.jedis","Jedis",true,"eval","(String,List,List)","","Argument[0]","code-injection","manual"]
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data: []
- addsTo:
pack: codeql/java-all
extensible: neutralModel
data: []

5
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"codeQL.cli.executablePath": "/Users/hohn/work-gh/codeql-lab/codeql/codeql",
"codeQL.runningQueries.useExtensionPacks": "all"
}

View File

@@ -175,3 +175,187 @@
#+END_SRC
* Modeling sqlite as dependency
The tree
: src-sqlite
contains a trivial sample taken from a workshop. It uses
=sqlite-jdbc-3.36.0.1.jar=, so we can use it to illustrate modeling on a smaller
example.
* Modeling jedis as dependency
Running the model editor a jedis db models jedis dependencies; we need jedis
/as/ dependency to model it.
Using the
- model as depedency option
the query run by model editor is
: /Users/hohn/work-gh/codeql-lab/ql/java/ql/src/utils/modeleditor/FrameworkModeEndpoints.ql
The columns of the query
#+BEGIN_SRC java
from PublicEndpointFromSource endpoint, boolean supported, string type
where
supported = isSupported(endpoint) and
type = supportedType(endpoint)
select endpoint, endpoint.getPackageName(), endpoint.getTypeName(), endpoint.getName(),
endpoint.getParameterTypes(), supported,
endpoint.getCompilationUnit().getParentContainer().getBaseName(), type
#+END_SRC
indicate the modeling state:
- supported == true :: shows as 'Method already modeled' in the editor
- supported == false :: shows as 'Unmodeled' in the editor
Files involved:
- Note that just by starting =CodeQL: Method modeling=, the new file
: .github/codeql/extensions/jedis-db-local-java/codeql-pack.yml
is created.
- After selection and saving, results are in
: ~/work-gh/codeql-lab/.github/codeql/extensions/jedis-db-local-java/models/redis.clients.jedis.model.yml
The sink added:
#+BEGIN_SRC yaml
extensions:
...
- addsTo:
pack: codeql/java-all
extensible: sinkModel
data:
- ["redis.clients.jedis","Jedis",true,"eval","(String)","","Argument[0]","code-injection","manual"]
...
#+END_SRC
For the files to be picked up requires the entry
: "codeQL.runningQueries.useExtensionPacks": "all"
in
: /Users/hohn/work-gh/codeql-lab/qllab.code-workspace
#+begin_src javascript
{
"folders": [
{
"path": "."
}
],
"settings": {
"omnisharp.autoStart": false,
"codeQL.githubDatabase.download": "never",
"sarif-viewer.connectToGithubCodeScanning": "off",
"codeQL.cli.executablePath": "/Users/hohn/work-gh/codeql-lab/codeql/codeql",
"codeQL.runningQueries.useExtensionPacks": "all"
}
}
#+end_src
In some cases (older vs code?), the file
: /Users/hohn/work-gh/codeql-lab/.vscode/settings.json
needs that entry.
With the additions from the model editor, the query
#+BEGIN_SRC java
import java
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.DataFlow
from DataFlow::Node n, string type
where sinkNode(n, type)
and type = "code-injection"
select n, type
#+END_SRC
lists the sink arguments to eval():
example.ql on jedis-db-local - finished in 2 seconds (14 results) [7/8/2025, 12:51:20 PM]
| 1 | script | code-injection |
| 2 | getBytes(...) | code-injection |
| 3 | script | code-injection |
| 4 | script | code-injection |
| 5 | script | code-injection |
| 6 | script | code-injection |
| 7 | "return redis.call('get','foo')" | code-injection |
| 8 | "return redis.call('get','foo')" | code-injection |
| 9 | encode(...) | code-injection |
| 10 | encode(...) | code-injection |
| 11 | "return redis.call('get','foo')" | code-injection |
| 12 | "return redis.call('get','foo')" | code-injection |
| 13 | script | code-injection |
| 14 | "return {}" | code-injection |
* TODO Use of the models in existing queries can be checked
#+BEGIN_SRC sh
rg -l -- '-injection' ql/java |grep '\.qll*'
hohn@ghm3 ~/work-gh/codeql-lab
2:$ rg -l -- '-injection' ql/java |grep '\.qll*'
ql/java/ql/src/Security/CWE/CWE-643/XPathInjection.ql
ql/java/ql/src/Security/CWE/CWE-078/ExecTainted.ql
ql/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql
ql/java/ql/src/Security/CWE/CWE-117/LogInjection.ql
ql/java/ql/src/Security/CWE/CWE-470/FragmentInjection.ql
ql/java/ql/src/Security/CWE/CWE-470/FragmentInjectionInPreferenceActivity.ql
ql/java/ql/src/Security/CWE/CWE-730/RegexInjection.ql
ql/java/ql/lib/semmle/code/java/security/XsltInjection.qll
ql/java/ql/src/Security/CWE/CWE-090/LdapInjection.ql
ql/java/ql/lib/semmle/code/java/security/GroovyInjection.qll
ql/java/ql/lib/semmle/code/java/security/XPath.qll
ql/java/ql/lib/semmle/code/java/security/TaintedEnvironmentVariableQuery.qll
ql/java/ql/src/Security/CWE/CWE-074/XsltInjection.ql
ql/java/ql/src/Security/CWE/CWE-074/JndiInjection.ql
ql/java/ql/lib/semmle/code/java/security/MvelInjection.qll
ql/java/ql/src/Security/CWE/CWE-089/SqlTainted.ql
ql/java/ql/lib/semmle/code/java/security/QueryInjection.qll
ql/java/ql/lib/semmle/code/java/security/CsrfUnprotectedRequestTypeQuery.qll
ql/java/ql/lib/semmle/code/java/security/ZipSlipQuery.qll
ql/java/ql/src/Security/CWE/CWE-917/OgnlInjection.ql
ql/java/ql/lib/semmle/code/java/security/SensitiveLoggingQuery.qll
ql/java/ql/lib/semmle/code/java/security/LdapInjection.qll
ql/java/ql/lib/semmle/code/java/security/TaintedPathQuery.qll
ql/java/ql/lib/semmle/code/java/security/JexlInjectionQuery.qll
ql/java/ql/lib/semmle/code/java/security/LogInjection.qll
ql/java/ql/lib/semmle/code/java/security/TemplateInjection.qll
ql/java/ql/lib/ext/org.apache.hadoop.hive.ql.metadata.model.yml
ql/java/ql/lib/semmle/code/java/security/XSS.qll
ql/java/ql/lib/semmle/code/java/security/JndiInjection.qll
ql/java/ql/lib/semmle/code/java/security/FragmentInjection.qll
ql/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll
ql/java/ql/src/Security/CWE/CWE-094/SpelInjection.ql
ql/java/ql/lib/semmle/code/java/security/CleartextStorageAndroidFilesystemQuery.qll
ql/java/ql/src/Security/CWE/CWE-094/JexlInjection.ql
ql/java/ql/src/Security/CWE/CWE-094/MvelInjection.ql
ql/java/ql/src/Security/CWE/CWE-094/TemplateInjection.ql
ql/java/ql/src/Security/CWE/CWE-094/GroovyInjection.ql
ql/java/ql/lib/semmle/code/java/security/OgnlInjection.qll
ql/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExecLocal.ql
ql/java/ql/src/experimental/Security/CWE/CWE-078/CommandInjectionRuntimeExec.ql
ql/java/ql/lib/ext/org.apache.hadoop.hive.ql.exec.model.yml
ql/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql
ql/java/ql/src/experimental/Security/CWE/CWE-094/BeanShellInjection.ql
ql/java/ql/src/Frameworks/Spring/Violations of Best Practice/UseSetterInjection.ql
ql/java/ql/src/experimental/Security/CWE/CWE-094/JythonInjection.ql
ql/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
ql/java/ql/src/experimental/Security/CWE/CWE-094/JakartaExpressionInjection.ql
ql/java/ql/src/experimental/Security/CWE/CWE-094/JShellInjection.ql
ql/java/ql/src/experimental/Security/CWE/CWE-200/AndroidFileIntentSink.qll
ql/java/ql/src/experimental/Security/CWE/CWE-652/XQueryInjection.ql
ql/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql
ql/java/ql/src/experimental/Security/CWE/CWE-020/Log4jJndiInjection.ql
ql/java/ql/src/experimental/Security/CWE/CWE-089/MyBatisMapperXmlSqlInjection.ql
ql/java/ql/src/experimental/Security/CWE/CWE-089/MyBatisAnnotationSqlInjection.ql
ql/java/ql/src/utils/modelgenerator/internal/CaptureModels.qll
#+END_SRC
#+BEGIN_SRC text
hohn@ghm3 ~/work-gh/codeql-lab
0:$ rg -l -- '-injection' ql/cpp |grep '\.qll*'
ql/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql
ql/cpp/ql/src/Security/CWE/CWE-022/TaintedPath.ql
ql/cpp/ql/src/experimental/Security/CWE/CWE-078/WordexpTainted.ql
ql/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
#+END_SRC
* TODO for java, the sqltainted query will find the sink, not the source yet.
* TODO vulnerable sample
For .eval() to show in a query, it has to be used in an application. So we
modify src-sqlite/AddUser.java for jedis.

View File

@@ -1,12 +1,13 @@
/**
* @name Empty block
* @kind problem
* @problem.severity warning
* @id java/example/empty-block
*/
import java
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.DataFlow
from BlockStmt b
where b.getNumStmt() = 0
select b, "This is an empty block."
from DataFlow::Node n, string type
where sinkNode(n, type)
and type = "code-injection"
select n, type
// // See if we have calls to eval() in the library
// from Call c
// where c.getCallee().hasQualifiedName("redis.clients.jedis","Jedis", "eval")
// select c

View File

@@ -0,0 +1,45 @@
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class AddUser {
public static Connection connect() {
Connection conn = null;
try {
String url = "jdbc:sqlite:users.sqlite";
conn = DriverManager.getConnection(url);
System.out.println("Connected...");
} catch (SQLException e) {
System.out.println(e.getMessage());
}
return conn;
}
static String get_user_info() {
System.out.println("Enter name:");
return System.console().readLine();
}
static void write_info(int id, String info) {
try (Connection conn = connect()) {
String query = String.format("INSERT INTO users VALUES (%d, '%s')", id, info);
conn.createStatement().executeUpdate(query);
System.err.printf("Sent: %s", query);
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
static int get_new_id() {
return (int)(Math.random()*100000);
}
public static void main(String[] args) {
String info;
int id;
info = get_user_info();
id = get_new_id();
write_info(id, info);
}
}

View File

@@ -0,0 +1,199 @@
* SQL injection example
This directory contains the problematic Java source code. The rest of this
README describes
- the [[*Setup and sample run][Setup and sample run]] for the problem,
- briefly describes how to [[*Identify the problem][Identify the problem]] and
- instructions to [[*Build the codeql database][Build the codeql database]]
The codeql query is developed in [[../session/README.org]].
** Setup and sample run
The jdbc connector at https://github.com/xerial/sqlite-jdbc, from [[https://github.com/xerial/sqlite-jdbc/releases/download/3.36.0.1/sqlite-jdbc-3.36.0.1.jar][here]] is
included in the git repository.
#+BEGIN_SRC sh
# Use a simple headline prompt
PS1='
\033[32m---- SQL injection demo ----\[\033[33m\033[0m\]
$?:$ '
# Build
./build.sh
# Prepare db
./admin -r
./admin -c
./admin -s
# Add regular user interactively
./add-user 2>> users.log
First User
# Check
./admin -s
# Add Johnny Droptable
./add-user 2>> users.log
Johnny'); DROP TABLE users; --
# And the problem:
./admin -s
# Check the log
tail users.log
#+END_SRC
** Identify the problem
=./add-user= is reading from =STDIN=, and writing to a database; looking at the code in
[[./AddUser.java]] leads to
: System.console().readLine();
for the read and
: conn.createStatement().executeUpdate(query);
for the write.
This problem is thus a dataflow problem; in codeql terminology we have
- a /source/ at the =System.console().readLine();=
- a /sink/ at the =conn.createStatement().executeUpdate(query);=
We write codeql to identify these two, and then connect them via
- a /dataflow configuration/ -- for this problem, the more general /taintflow
configuration/.
** Build the codeql database
To get started, build the codeql database (adjust paths to your setup):
#+BEGIN_SRC sh
# Build the db with source commit id.
SRCDIR=$(pwd)
DB=$SRCDIR/java-sqli-$(cd $SRCDIR && git rev-parse --short HEAD)
echo $DB
test -d "$DB" && rm -fR "$DB"
mkdir -p "$DB"
cd $SRCDIR && codeql database create --language=java -s . -j 8 -v $DB --command='./build.sh'
# Check for AddUser in the db
unzip -v $DB/src.zip | grep AddUser
#+END_SRC
Then add this database directory to your VS Code =DATABASES= tab.
** (optional) Build the codeql database in steps
For larger projects, using a single command to build everything is costly when
any part of the build fails. The sequence here is also used by the GHAS
default setup, so familiarity with it helps in reviewing logs.
The purpose of these sections is to illustrate the codeql commands used in
default setup and making the connection between the GHAS default action and the
CodeQL CLI explicit.
After running default setup and downloading the log, you will see the following
entries embedded in the full log. They are repeated here for completeness; you
can skip the command-line options for now.
#+BEGIN_SRC sh
codeql version --format=json
codeql resolve languages --format=betterjson --extractor-options-verbosity=4 --extractor-include-aliases
codeql database init --force-overwrite --db-cluster /home/runner/work/_temp/codeql_databases --source-root=/home/runner/work/codeql-workshop-sql-injection-java/codeql-workshop-sql-injection-java --extractor-include-aliases --language=java --codescanning-config=/home/runner/work/_temp/user-config.yaml --build-mode=none --calculate-language-specific-baseline --sublanguage-file-coverage
codeql database trace-command --use-build-mode --working-dir /home/runner/work/codeql-workshop-sql-injection-java/codeql-workshop-sql-injection-java /home/runner/work/_temp/codeql_databases/java
codeql database finalize --finalize-dataset --threads=4 --ram=14567 /home/runner/work/_temp/codeql_databases/java
codeql database run-queries --ram=14567 --threads=4 /home/runner/work/_temp/codeql_databases/java --expect-discarded-cache --min-disk-free=1024 -v --intra-layer-parallelism
codeql database cleanup /home/runner/work/_temp/codeql_databases/java --cache-cleanup=brutal
codeql database bundle /home/runner/work/_temp/codeql_databases/java --output=/home/runner/work/_temp/codeql_databases/java.zip --name=java
#+END_SRC
To build a database in steps locally, use the following sequence, adjusting
paths to your setup:
#+BEGIN_SRC sh
# Build the db with source commit id.
SRCDIR=$HOME/local/codeql-workshop-sql-injection-java/src
DB=$SRCDIR/java-sqli-$(cd $SRCDIR && git rev-parse --short HEAD)
# Check paths
echo "DB will be: $DB"
echo "SRC is in: $SRCDIR"
# Prepare db directory
test -d "$DB" && rm -fR "$DB"
mkdir -p "$DB"
# Run the build, without --db-cluster
# Init database
cd $SRCDIR
codeql database init \
--language=java \
--build-mode=none \
--source-root=. \
-v $DB
# Repeat trace-command as needed to cover all targets
codeql database trace-command \
--use-build-mode \
--working-dir . \
$DB
# Finalize database
codeql database finalize \
--finalize-dataset \
--threads=4 \
--ram=14567 \
$DB
# Use the database; get the location
echo $DB
# /Users/hohn/local/codeql-workshop-sql-injection-java/src/java-sqli-161a1d5
#+END_SRC
To also analyze the database just built, we use the log's command but add an
explicit query name:
#+BEGIN_SRC sh
codeql database run-queries \
--ram=14567 \
--threads=4 $DB \
--expect-discarded-cache \
--min-disk-free=1024 \
-v \
--intra-layer-parallelism \
-- \
../session/simple.ql
#+END_SRC
This only gives us a bqrs file, we want sarif. Checking help:
#+BEGIN_SRC text
codeql database run-queries --help
Usage: codeql database run-queries [OPTIONS] -- <database> [<query|dir|suite|pack>...]
[Plumbing] Run a set of queries together.
Run one or more queries against a CodeQL database, saving the results to the results
subdirectory of the database directory.
The results can later be converted to readable formats by codeql database interpret-results,
or query-for-query by with codeql bqrs decode or codeql bqrs interpret.
#+END_SRC
So we run the following
#+BEGIN_SRC sh
VERSION=$(cd $SRCDIR && git rev-parse --short HEAD)
codeql database interpret-results \
--format=sarifv2.1.0 \
-o simple-$VERSION.sarif \
-- $DB ../session/simple.ql
echo "Results in simple-$VERSION.sarif"
#+END_SRC
We kept the output for this sample in [[./simple-161a1d5.sarif]]

View File

@@ -0,0 +1,3 @@
#!/bin/sh
java -cp ".:sqlite-jdbc-3.36.0.1.jar" AddUser $@

60
codeql-jedis/src-sqlite/admin Executable file
View File

@@ -0,0 +1,60 @@
#!/bin/bash
set -e
script=$(basename "$0")
GREEN='\033[0;32m'
MAGENTA='\033[0;95m'
NC='\033[0m'
RED='\033[0;31m'
YELLOW='\033[0;33m'
help() {
echo -e "Usage: ./${script} [options]" \
"\n${YELLOW}Options: ${NC}" \
"\n\t -h ${GREEN}Show Help ${NC}" \
"\n\t -c ${MAGENTA}Creates a users table ${NC}" \
"\n\t -s ${MAGENTA}Shows all records in the users table ${NC}" \
"\n\t -r ${RED}Removes users table ${NC}"
}
remove-db () {
rm users.sqlite
}
create-db () {
echo '
CREATE TABLE users (
user_id INTEGER not null,
name TEXT NOT NULL
);
' | sqlite3 users.sqlite
}
show-db () {
echo '
SELECT * FROM users;
' | sqlite3 users.sqlite
}
if [ $# == 0 ]; then
help
exit 0
fi
while getopts "h?csr" option
do
case "${option}"
in
h|\?)
help
exit 0
;;
c) create-db
;;
s) show-db
;;
r) remove-db
;;
esac
done

View File

@@ -0,0 +1,3 @@
#!/bin/bash
javac -cp "sqlite-jdbc-3.36.0.1.jar" AddUser.java

View File

@@ -0,0 +1,60 @@
# -*- sh -*-
.runs | .[] | .results | .[] |
( (.ruleId, ": ",
(.message.text | split("\n") | ( .[0], " [", length-1 , " more]")),
"\n")
,
(if (.codeFlows != null) then
(.codeFlows | .[] |
(" Path\n"
,
( .threadFlows | .[] | .locations | .[] | .location | " "
,
( .physicalLocation | ( .artifactLocation.uri, ":", .region.startLine, ":"))
,
(.message.text, " ")
,
"\n"
)))
else
(.locations | .[] |
( " "
,
(.physicalLocation | ( .artifactLocation.uri, ":", .region.startLine, ":"))
))
,
# .message.text,
"\n"
end)
) | tostring
# This script extracts the following parts of the sarif output:
#
# # problem
# "runs" : [ {
# "results" : [ {
# "ruleId" : "cpp/UncheckedErrorCode",
# # path problem
# "runs" : [ {
# "tool" : {
# "driver" : {
# "rules" : [ {
# "properties" : {
# "kind" : "path-problem",
# "runs" : [ {
# "results" : [ {
# "ruleId" : "cpp/DangerousArithmetic",
# "ruleIndex" : 6,
# "message" : {
# "text" : "Potential overflow (conversion: int -> unsigned int)\nPotential overflow (con
# "runs" : [ {
# "results" : [ {
# "codeFlows" : [ {
# "threadFlows" : [ {
# "locations" : [ {
# "location" : {
# "message" : {
# "text" : "buff"

BIN
codeql-jedis/src-sqlite/sqlite-jdbc-3.36.0.1.jar (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -5,6 +5,10 @@
}
],
"settings": {
"omnisharp.autoStart": false
"omnisharp.autoStart": false,
"codeQL.githubDatabase.download": "never",
"sarif-viewer.connectToGithubCodeScanning": "off",
"codeQL.cli.executablePath": "/Users/hohn/work-gh/codeql-lab/codeql/codeql",
"codeQL.runningQueries.useExtensionPacks": "all"
}
}