mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
Merge branch 'main' into fix/update-gson-model
This commit is contained in:
@@ -1,31 +1,129 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Before running this, make sure there is an SSO-enabled token with package:write
|
||||
# permissions to codeql supplied via the GITHUB_TOKEN environment variable
|
||||
|
||||
AUTOMODEL_ROOT="$(readlink -f "$(dirname $0)")"
|
||||
WORKSPACE_ROOT="$AUTOMODEL_ROOT/../../.."
|
||||
GRPS="automodel,-test"
|
||||
|
||||
if [ -z "$CODEQL_DIST" ]; then
|
||||
echo "CODEQL_DIST not set"
|
||||
exit -1
|
||||
# Add help message
|
||||
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||
echo "Usage: ./publish [override-release]"
|
||||
echo "By default we publish the version of the codeql repo specified by the latest official release defined by the codeml-automodel repo."
|
||||
echo "Otherwise, the optional argument override-release forces your current HEAD to be published."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd "$AUTOMODEL_ROOT"
|
||||
# Check that either there are 0 or 1 arguments, and if 1 argument then check that it is "override-release"
|
||||
if [ $# -gt 1 ] || [ $# -eq 1 ] && [ "$1" != "override-release" ]; then
|
||||
echo "Error: Invalid arguments. Please run './publish --help' for usage information."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If we're publishing the codeml-automodel release then we will checkout the sha specified in the release.
|
||||
# So we need to check that there are no uncommitted changes in the local branch.
|
||||
# And, if we're publishing the current HEAD, it's cleaner to ensure that there are no uncommitted changes.
|
||||
if ! git diff --quiet; then
|
||||
echo "Error: Uncommitted changes exist. Please commit or stash your changes before publishing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check the above environment variables are set
|
||||
if [ -z "${GITHUB_TOKEN}" ]; then
|
||||
echo "Error: GITHUB_TOKEN environment variable not set. Please set this to a token with package:write permissions to codeql."
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${CODEQL_DIST}" ]; then
|
||||
echo "Error: CODEQL_DIST environment variable not set. Please set this to the path of a codeql distribution."
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${GH_TOKEN}" ]; then
|
||||
echo "Error: GH_TOKEN environment variable not set. Please set this to a token with repo permissions to github/codeml-automodel."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the sha of the previous release, i.e. the last commit to the main branch that updated the query pack version
|
||||
PREVIOUS_RELEASE_SHA=$(git rev-list -n 1 main -- ./src/qlpack.yml)
|
||||
if [ -z "$PREVIOUS_RELEASE_SHA" ]; then
|
||||
echo "Error: Could not get the sha of the previous release of codeml-automodel query pack"
|
||||
exit 1
|
||||
else
|
||||
echo "Previous query-pack release sha: $PREVIOUS_RELEASE_SHA"
|
||||
fi
|
||||
|
||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
CURRENT_SHA=$(git rev-parse HEAD)
|
||||
|
||||
if [ -z "${1:-}" ]; then
|
||||
# If the first argument is empty, use the latest release of codeml-automodel
|
||||
TAG_NAME=$(gh api -H 'Accept: application/vnd.github+json' -H 'X-GitHub-Api-Version: 2022-11-28' /repos/github/codeml-automodel/releases/latest | jq -r .tag_name)
|
||||
# Check TAG_NAME is not empty
|
||||
if [ -z "$TAG_NAME" ]; then
|
||||
echo "Error: Could not get latest release of codeml-automodel"
|
||||
exit 1
|
||||
fi
|
||||
echo "Updating to latest automodel release: $TAG_NAME"
|
||||
# Before downloading, delete any existing release.zip, and ignore failure if not present
|
||||
rm release.zip || true
|
||||
gh release download $TAG_NAME -A zip -O release.zip --repo 'https://github.com/github/codeml-automodel'
|
||||
# Before unzipping, delete any existing release directory, and ignore failure if not present
|
||||
rm -rf release || true
|
||||
unzip -o release.zip -d release
|
||||
REVISION=$(jq -r '.["codeql-sha"]' release/codeml-automodel*/codeml-automodel-release.json)
|
||||
echo "The latest codeml-automodel release specifies the codeql sha $REVISION"
|
||||
# Check that REVISION is downstream from PREVIOUS_RELEASE_SHA
|
||||
if ! git merge-base --is-ancestor "$PREVIOUS_RELEASE_SHA" "$REVISION"; then
|
||||
echo "Error: The codeql version $REVISION is not downstream of the query-pack version $PREVIOUS_RELEASE_SHA"
|
||||
exit 1
|
||||
fi
|
||||
# Get the version of the codeql code specified by the codeml-automodel release
|
||||
git checkout "$REVISION"
|
||||
else
|
||||
# Check that the current HEAD is downstream from PREVIOUS_RELEASE_SHA
|
||||
if ! git merge-base --is-ancestor "$PREVIOUS_RELEASE_SHA" "$CURRENT_SHA"; then
|
||||
echo "Error: The current HEAD is not downstream from the previous release"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get the absolute path of the automodel repo
|
||||
AUTOMODEL_ROOT="$(readlink -f "$(dirname $0)")"
|
||||
# Get the absolute path of the workspace root
|
||||
WORKSPACE_ROOT="$AUTOMODEL_ROOT/../../.."
|
||||
# Specify the groups of queries to test and publish
|
||||
GRPS="automodel,-test"
|
||||
|
||||
pushd "$AUTOMODEL_ROOT"
|
||||
echo Testing automodel queries
|
||||
"${CODEQL_DIST}/codeql" test run test
|
||||
popd
|
||||
|
||||
cd "$WORKSPACE_ROOT"
|
||||
pushd "$WORKSPACE_ROOT"
|
||||
echo "Preparing the release"
|
||||
"${CODEQL_DIST}/codeql" pack release --groups $GRPS -v
|
||||
|
||||
echo Preparing release
|
||||
"${CODEQL_DIST}/codeql" pack release --groups $GRPS
|
||||
echo "Publishing the release"
|
||||
# Add --dry-run to test publishing
|
||||
"${CODEQL_DIST}/codeql" pack publish --groups $GRPS -v
|
||||
|
||||
echo Publishing automodel
|
||||
"${CODEQL_DIST}/codeql" pack publish --groups $GRPS
|
||||
echo "Bumping versions"
|
||||
"${CODEQL_DIST}/codeql" pack post-release --groups $GRPS -v
|
||||
popd
|
||||
|
||||
echo Bumping versions
|
||||
"${CODEQL_DIST}/codeql" pack post-release --groups $GRPS
|
||||
# The above commands update
|
||||
# ./src/CHANGELOG.md
|
||||
# ./src/codeql-pack.release.yml
|
||||
# ./src/qlpack.yml
|
||||
# and add a new file
|
||||
# ./src/change-notes/released/<version>.md
|
||||
|
||||
if [ -z "${1:-}" ]; then
|
||||
# If we used the latest release of codeml-automodel, then we need to return to the current branch
|
||||
git checkout "$CURRENT_BRANCH"
|
||||
fi
|
||||
|
||||
# Add the updated files to the current branch
|
||||
git add ./src/CHANGELOG.md
|
||||
git add ./src/codeql-pack.release.yml
|
||||
git add ./src/qlpack.yml
|
||||
git add ./src/change-notes/released/*
|
||||
echo "Added the following updated version files to the current branch:"
|
||||
git status -s
|
||||
|
||||
echo "Automodel packs successfully published. Local files have been modified. Please commit and push the version changes and then merge into main."
|
||||
|
||||
echo Automodel packs successfully published. Please commit and push the version changes.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.0.11
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.0.10
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -65,7 +65,7 @@ A significant part of the behavior of extraction queries is implemented in share
|
||||
|
||||
## Packaging
|
||||
|
||||
Automodel extraction queries come as a dedicated package. See [qlpack.yml](https://github.com/github/codeql/blob/main/java/ql/automodel/src/qlpack.yml). The [publish.sh](https://github.com/github/codeql/blob/main/java/ql/automodel/publish.sh) script is responsible for publishing a new version to the [package registry](https://github.com/orgs/codeql/packages/container/package/java-automodel-queries).
|
||||
Automodel extraction queries come as a dedicated package. See [qlpack.yml](https://github.com/github/codeql/blob/main/java/ql/automodel/src/qlpack.yml). The [publish.sh](https://github.com/github/codeql/blob/main/java/ql/automodel/publish.sh) script is responsible for publishing a new version to the [package registry](https://github.com/orgs/codeql/packages/container/package/java-automodel-queries). **The extraction queries are functionally coupled with other automodel components. Only publish the query pack as part of the automodel release process.**
|
||||
|
||||
### Backwards Compatibility
|
||||
|
||||
|
||||
3
java/ql/automodel/src/change-notes/released/0.0.11.md
Normal file
3
java/ql/automodel/src/change-notes/released/0.0.11.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.0.11
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.10
|
||||
lastReleaseVersion: 0.0.11
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-automodel-queries
|
||||
version: 0.0.11-dev
|
||||
version: 0.0.12-dev
|
||||
groups:
|
||||
- java
|
||||
- automodel
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
https://repo.maven.apache.org/maven2/com/feiniaojin/naaf/naaf-graceful-response-example/1.0/naaf-graceful-response-example-1.0.jar
|
||||
https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/avro-registry-in-source-tests/1.8/avro-registry-in-source-tests-1.8.jar
|
||||
https://repo.maven.apache.org/maven2/com/github/MoebiusSolutions/avro-registry-in-source/example-project/1.5/example-project-1.5.jar
|
||||
https://repo.maven.apache.org/maven2/com/intuit/benten/benten-examples/0.1.5/benten-examples-0.1.5.jar
|
||||
https://repo.maven.apache.org/maven2/com/jakewharton/twirl/sample-runtime/1.2.0/sample-runtime-1.2.0.jar
|
||||
https://repo.maven.apache.org/maven2/com/mattunderscore/code/generation/specky/plugin-example/0.8.0/plugin-example-0.8.0.jar
|
||||
https://repo.maven.apache.org/maven2/com/microsoft/tang/tang-test-jarAB/0.9/tang-test-jarAB-0.9.jar
|
||||
https://repo.maven.apache.org/maven2/de/knutwalker/rx-redis-example_2.11/0.1.2/rx-redis-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/streamnative/com/example/maven-central-template/server/3.0.0/server-3.0.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-inject/0.5.10/minijax-example-inject-0.5.10.jar
|
||||
https://repo.maven.apache.org/maven2/org/minijax/minijax-example-json/0.5.10/minijax-example-json-0.5.10.jar
|
||||
https://repo.maven.apache.org/maven2/org/minijax/minijax-example-mustache/0.5.10/minijax-example-mustache-0.5.10.jar
|
||||
https://repo.maven.apache.org/maven2/org/minijax/minijax-example-petclinic/0.5.10/minijax-example-petclinic-0.5.10.jar
|
||||
https://repo.maven.apache.org/maven2/org/minijax/minijax-example-security/0.5.10/minijax-example-security-0.5.10.jar
|
||||
https://repo.maven.apache.org/maven2/org/minijax/minijax-example-ssl/0.5.10/minijax-example-ssl-0.5.10.jar
|
||||
https://repo.maven.apache.org/maven2/org/minijax/minijax-example-todo-backend/0.5.10/minijax-example-todo-backend-0.5.10.jar
|
||||
https://repo.maven.apache.org/maven2/org/minijax/minijax-example-websocket/0.5.10/minijax-example-websocket-0.5.10.jar
|
||||
https://repo.maven.apache.org/maven2/org/scalamock/scalamock-examples_2.10/3.6.0/scalamock-examples_2.10-3.6.0.jar
|
||||
https://repo.maven.apache.org/maven2/org/somda/sdc/glue-examples/4.0.0/glue-examples-4.0.0.jar
|
||||
https://repo.maven.apache.org/maven2/us/fatehi/schemacrawler-examplecode/16.20.2/schemacrawler-examplecode-16.20.2.jar
|
||||
https://repo1.maven.org/maven2/junit/junit/4.11/junit-4.11.jar
|
||||
https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.14.0/commons-lang3-3.14.0.jar
|
||||
https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"markdownMessage": "Java buildless mode used build tool Maven to pick a JDK version and/or to recommend external dependencies.",
|
||||
"severity": "unknown",
|
||||
"source": {
|
||||
"extractorName": "java",
|
||||
"id": "java/autobuilder/buildless/using-build-tool-advice",
|
||||
"name": "Java buildless mode used build tool Maven to pick a JDK version and/or to recommend external dependencies"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": false,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"markdownMessage": "Java buildless mode used the system default JDK.",
|
||||
"severity": "unknown",
|
||||
"source": {
|
||||
"extractorName": "java",
|
||||
"id": "java/autobuilder/buildless/jdk-system-default",
|
||||
"name": "Java buildless mode used the system default JDK"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": false,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"markdownMessage": "Java was extracted in buildless mode. This means that all Java source in the working directory will be scanned, with build tools such as Maven and Gradle only contributing information about external dependencies.",
|
||||
"severity": "note",
|
||||
"source": {
|
||||
"extractorName": "java",
|
||||
"id": "java/autobuilder/buildless/mode-active",
|
||||
"name": "Java was extracted in buildless mode"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": true,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"markdownMessage": "Reading the dependency graph from Maven build files provided 3 classpath entries",
|
||||
"severity": "unknown",
|
||||
"source": {
|
||||
"extractorName": "java",
|
||||
"id": "java/autobuilder/buildless/depgraph-provided-by-maven",
|
||||
"name": "Java buildless mode extracted precise dependency graph information from Maven"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": false,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>maven-sample</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>maven-sample</name>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
|
||||
<plugins>
|
||||
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
|
||||
<plugin>
|
||||
<artifactId>maven-clean-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</plugin>
|
||||
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>2.5.2</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</plugin>
|
||||
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
|
||||
<plugin>
|
||||
<artifactId>maven-site-plugin</artifactId>
|
||||
<version>3.7.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>submod1</module>
|
||||
<module>submod2</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>submod1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>maven-sample-submod1</name>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
<parent>
|
||||
<artifactId>maven-sample</artifactId>
|
||||
<groupId>com.example</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.example;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* Hello world!
|
||||
*
|
||||
*/
|
||||
public class App
|
||||
{
|
||||
public static void main( String[] args )
|
||||
{
|
||||
System.out.println( "Hello World!" );
|
||||
String expectedVersion = System.getenv("EXPECT_MAVEN");
|
||||
Path mavenHome = Paths.get(System.getProperty("maven.home")).normalize();
|
||||
String observedVersion = mavenHome.getFileName().toString();
|
||||
if (expectedVersion != null && !expectedVersion.equals(observedVersion)) {
|
||||
System.err.println("Wrong maven version, expected '" + expectedVersion + "' but got '" + observedVersion + "'" + mavenHome);
|
||||
System.exit(1);
|
||||
}
|
||||
String commandMatcher = System.getenv("EXPECT_COMMAND_REGEX");
|
||||
String command = System.getProperty("sun.java.command");
|
||||
if (commandMatcher != null && !Pattern.matches(commandMatcher, command)) {
|
||||
System.err.println("Wrong command line, '" + command + "' does not match '" + commandMatcher + "'");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
version=1.0
|
||||
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>A sample</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello world!</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<struts>
|
||||
This is a sample file
|
||||
</struts>
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.example;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
*/
|
||||
public class AppTest
|
||||
{
|
||||
/**
|
||||
* Rigorous Test :-)
|
||||
*/
|
||||
@Test
|
||||
public void shouldAnswerWithTrue()
|
||||
{
|
||||
assertTrue( true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>submod2</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<name>maven-sample-submod2</name>
|
||||
<!-- FIXME change it to the project's website -->
|
||||
<url>http://www.example.com</url>
|
||||
|
||||
<parent>
|
||||
<artifactId>maven-sample</artifactId>
|
||||
<groupId>com.example</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.7</maven.compiler.source>
|
||||
<maven.compiler.target>1.7</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- depend on sibling's main and test jars -->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>submod1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>submod1</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Add a module-specific dependency -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.14.0</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.example;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* Hello world!
|
||||
*
|
||||
*/
|
||||
public class App2
|
||||
{
|
||||
public static void main( String[] args )
|
||||
{
|
||||
System.out.println( "Hello World!" );
|
||||
String expectedVersion = System.getenv("EXPECT_MAVEN");
|
||||
Path mavenHome = Paths.get(System.getProperty("maven.home")).normalize();
|
||||
String observedVersion = mavenHome.getFileName().toString();
|
||||
if (expectedVersion != null && !expectedVersion.equals(observedVersion)) {
|
||||
System.err.println("Wrong maven version, expected '" + expectedVersion + "' but got '" + observedVersion + "'" + mavenHome);
|
||||
System.exit(1);
|
||||
}
|
||||
String commandMatcher = System.getenv("EXPECT_COMMAND_REGEX");
|
||||
String command = System.getProperty("sun.java.command");
|
||||
if (commandMatcher != null && !Pattern.matches(commandMatcher, command)) {
|
||||
System.err.println("Wrong command line, '" + command + "' does not match '" + commandMatcher + "'");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
version=1.0
|
||||
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>A sample</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello world!</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<struts>
|
||||
This is a sample file
|
||||
</struts>
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.example;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
*/
|
||||
public class AppTest2
|
||||
{
|
||||
/**
|
||||
* Rigorous Test :-)
|
||||
*/
|
||||
@Test
|
||||
public void shouldAnswerWithTrue()
|
||||
{
|
||||
assertTrue( true );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#select
|
||||
| submod1/src/main/java/com/example/App.java:0:0:0:0 | App |
|
||||
| submod1/src/test/java/com/example/AppTest.java:0:0:0:0 | AppTest |
|
||||
| submod2/src/main/java/com/example/App2.java:0:0:0:0 | App2 |
|
||||
| submod2/src/test/java/com/example/AppTest2.java:0:0:0:0 | AppTest2 |
|
||||
xmlFiles
|
||||
| pom.xml:0:0:0:0 | pom.xml |
|
||||
| submod1/pom.xml:0:0:0:0 | submod1/pom.xml |
|
||||
| submod1/src/main/resources/page.xml:0:0:0:0 | submod1/src/main/resources/page.xml |
|
||||
| submod1/src/main/resources/struts.xml:0:0:0:0 | submod1/src/main/resources/struts.xml |
|
||||
| submod2/pom.xml:0:0:0:0 | submod2/pom.xml |
|
||||
| submod2/src/main/resources/page.xml:0:0:0:0 | submod2/src/main/resources/page.xml |
|
||||
| submod2/src/main/resources/struts.xml:0:0:0:0 | submod2/src/main/resources/struts.xml |
|
||||
propertiesFiles
|
||||
| submod1/src/main/resources/my-app.properties:0:0:0:0 | submod1/src/main/resources/my-app.properties |
|
||||
| submod2/src/main/resources/my-app.properties:0:0:0:0 | submod2/src/main/resources/my-app.properties |
|
||||
| test-db/log/ext/javac.properties:0:0:0:0 | test-db/log/ext/javac.properties |
|
||||
@@ -0,0 +1,8 @@
|
||||
from create_database_utils import *
|
||||
from diagnostics_test_utils import *
|
||||
from buildless_test_utils import *
|
||||
|
||||
run_codeql_database_create([], lang="java", extra_env={"CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS": "true", "CODEQL_EXTRACTOR_JAVA_OPTION_BUILDLESS_CLASSPATH_FROM_BUILD_FILES": "true"})
|
||||
|
||||
check_diagnostics()
|
||||
check_buildless_fetches()
|
||||
@@ -0,0 +1,9 @@
|
||||
import java
|
||||
|
||||
from File f
|
||||
where f.isSourceFile()
|
||||
select f
|
||||
|
||||
query predicate xmlFiles(XmlFile x) { any() }
|
||||
|
||||
query predicate propertiesFiles(File f) { f.getExtension() = "properties" }
|
||||
@@ -32,8 +32,11 @@ extensions:
|
||||
- ["hudson", "FilePath", True, "write", "(String,String)", "", "Argument[0]", "file-content-store", "manual"]
|
||||
- ["hudson", "Launcher$ProcStarter", False, "cmds", "", "", "Argument[0]", "command-injection", "manual"]
|
||||
- ["hudson", "Launcher$ProcStarter", False, "cmdAsSingleString", "", "", "Argument[0]", "command-injection", "manual"]
|
||||
- ["hudson", "Launcher$ProcStarter", False, "envs", "(String[])", "", "Argument[0]", "environment-injection", "manual"]
|
||||
- ["hudson", "Launcher", True, "launch", "", "", "Argument[0]", "command-injection", "manual"]
|
||||
- ["hudson", "Launcher", True, "decorateByEnv", "(String[])", "", "Argument[0]", "environment-injection", "manual"]
|
||||
- ["hudson", "Launcher", True, "launchChannel", "", "", "Argument[0]", "command-injection", "manual"]
|
||||
- ["hudson", "Launcher", True, "launchChannel", "", "", "Argument[3]", "environment-injection", "manual"]
|
||||
- ["hudson", "XmlFile", False, "XmlFile", "(XStream,File)", "", "Argument[1]", "path-injection", "ai-manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
|
||||
@@ -22,6 +22,8 @@ extensions:
|
||||
- ["java.lang", "Runtime", True, "exec", "(String,String[])", "", "Argument[0]", "command-injection", "ai-manual"]
|
||||
- ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[0]", "command-injection", "ai-manual"]
|
||||
- ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[2]", "command-injection", "ai-manual"]
|
||||
# All implementations of `java.lang.Runtime::exec` take the environment variables as their second argument.
|
||||
- ["java.lang", "Runtime", True, "exec", "", "", "Argument[1]", "environment-injection", "manual"]
|
||||
# These are potential vulnerabilities, but not for command-injection. No query for this kind of vulnerability currently exists.
|
||||
# - ["java.lang", "Runtime", False, "load", "(String)", "", "Argument[0]", "command-injection", "ai-manual"]
|
||||
# - ["java.lang", "Runtime", False, "loadLibrary", "(String)", "", "Argument[0]", "command-injection", "ai-manual"]
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["org.apache.commons.exec.environment", "EnvironmentUtils", True, "addVariableToEnvironment", "(Map,String)", "", "Argument[0]", "environment-injection", "manual"]
|
||||
@@ -0,0 +1,6 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["org.apache.commons.exec.launcher", "CommandLauncher", True, "exec", "", "", "Argument[1]", "environment-injection", "manual"]
|
||||
@@ -9,3 +9,5 @@ extensions:
|
||||
- ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String,boolean)", "", "Argument[0]", "command-injection", "manual"]
|
||||
- ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String[])", "", "Argument[0]", "command-injection", "manual"]
|
||||
- ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String[],boolean)", "", "Argument[0]", "command-injection", "manual"]
|
||||
- ["org.apache.commons.exec", "Executor", True, "execute", "(CommandLine,Map)", "", "Argument[1]", "environment-injection", "manual"]
|
||||
- ["org.apache.commons.exec", "Executor", True, "execute", "(CommandLine,Map,ExecuteResultHandler)", "", "Argument[1]", "environment-injection", "manual"]
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["org.pac4j.jwt.config.encryption", "SecretEncryptionConfiguration", True, "SecretEncryptionConfiguration", "", "", "Argument[0]", "credentials-key", "manual"]
|
||||
- ["org.pac4j.jwt.config.encryption", "SecretEncryptionConfiguration", True, "setSecret", "", "", "Argument[0]", "credentials-key", "manual"]
|
||||
- ["org.pac4j.jwt.config.encryption", "SecretEncryptionConfiguration", True, "setSecretBase64", "", "", "Argument[0]", "credentials-key", "manual"]
|
||||
- ["org.pac4j.jwt.config.encryption", "SecretEncryptionConfiguration", True, "setSecretBytes", "", "", "Argument[0]", "credentials-key", "manual"]
|
||||
9
java/ql/lib/ext/org.pac4j.jwt.config.signature.model.yml
Normal file
9
java/ql/lib/ext/org.pac4j.jwt.config.signature.model.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["org.pac4j.jwt.config.signature", "SecretSignatureConfiguration", True, "SecretEncryptionConfiguration", "", "", "Argument[0]", "credentials-key", "manual"]
|
||||
- ["org.pac4j.jwt.config.signature", "SecretSignatureConfiguration", True, "setSecret", "", "", "Argument[0]", "credentials-key", "manual"]
|
||||
- ["org.pac4j.jwt.config.signature", "SecretSignatureConfiguration", True, "setSecretBase64", "", "", "Argument[0]", "credentials-key", "manual"]
|
||||
- ["org.pac4j.jwt.config.signature", "SecretSignatureConfiguration", True, "setSecretBytes", "", "", "Argument[0]", "credentials-key", "manual"]
|
||||
23
java/ql/lib/semmle/code/java/frameworks/Netty.qll
Normal file
23
java/ql/lib/semmle/code/java/frameworks/Netty.qll
Normal file
@@ -0,0 +1,23 @@
|
||||
/** Provides definitions related to the Netty framework. */
|
||||
|
||||
import java
|
||||
|
||||
/** The interface `Cookie` in the packages `io.netty.handler.codec.http` and `io.netty.handler.codec.http.cookie`. */
|
||||
class NettyCookie extends Interface {
|
||||
NettyCookie() { this.hasQualifiedName("io.netty.handler.codec.http" + [".cookie", ""], "Cookie") }
|
||||
}
|
||||
|
||||
/** The class `DefaultCookie` in the packages `io.netty.handler.codec.http` and `io.netty.handler.codec.http.cookie`. */
|
||||
class NettyDefaultCookie extends Class {
|
||||
NettyDefaultCookie() {
|
||||
this.hasQualifiedName("io.netty.handler.codec.http" + [".cookie", ""], "DefaultCookie")
|
||||
}
|
||||
}
|
||||
|
||||
/** The method `setValue` of the interface `Cookie` or a class implementing it. */
|
||||
class NettySetCookieValueMethod extends Method {
|
||||
NettySetCookieValueMethod() {
|
||||
this.getDeclaringType*() instanceof NettyCookie and
|
||||
this.hasName("setValue")
|
||||
}
|
||||
}
|
||||
29
java/ql/lib/semmle/code/java/frameworks/OpenSaml.qll
Normal file
29
java/ql/lib/semmle/code/java/frameworks/OpenSaml.qll
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with the OpenSAML libraries.
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.security.InsecureRandomnessQuery
|
||||
|
||||
/** The interface `org.opensaml.saml.saml2.core.RequestAbstractType`. */
|
||||
class SamlRequestAbstractType extends Interface {
|
||||
SamlRequestAbstractType() {
|
||||
this.hasQualifiedName("org.opensaml.saml.saml2.core", "RequestAbstractType")
|
||||
}
|
||||
}
|
||||
|
||||
/** The method `setID` of the interface `RequestAbstractType`. */
|
||||
class SamlRequestSetIdMethod extends Method {
|
||||
SamlRequestSetIdMethod() {
|
||||
this.getDeclaringType() instanceof SamlRequestAbstractType and
|
||||
this.hasName("setID")
|
||||
}
|
||||
}
|
||||
|
||||
private class SamlRequestSetIdSink extends InsecureRandomnessSink {
|
||||
SamlRequestSetIdSink() {
|
||||
exists(MethodCall c | c.getMethod() instanceof SamlRequestSetIdMethod |
|
||||
c.getArgument(0) = this.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -244,7 +244,7 @@ class TypeCookie extends Class {
|
||||
}
|
||||
|
||||
/**
|
||||
* The method `getValue(String)` declared in `javax.servlet.http.Cookie`.
|
||||
* The method `getValue()` declared in `javax.servlet.http.Cookie`.
|
||||
*/
|
||||
class CookieGetValueMethod extends Method {
|
||||
CookieGetValueMethod() {
|
||||
@@ -254,6 +254,16 @@ class CookieGetValueMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The method `setValue(String)` declared in `javax.servlet.http.Cookie`.
|
||||
*/
|
||||
class CookieSetValueMethod extends Method {
|
||||
CookieSetValueMethod() {
|
||||
this.getDeclaringType() instanceof TypeCookie and
|
||||
this.hasName("setValue")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The method `getName()` declared in `javax.servlet.http.Cookie`.
|
||||
*/
|
||||
|
||||
32
java/ql/lib/semmle/code/java/security/Cookies.qll
Normal file
32
java/ql/lib/semmle/code/java/security/Cookies.qll
Normal file
@@ -0,0 +1,32 @@
|
||||
/** Provides definitions to reason about HTTP cookies. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.frameworks.Netty
|
||||
private import semmle.code.java.frameworks.Servlets
|
||||
|
||||
/** An expression setting the value of a cookie. */
|
||||
abstract class SetCookieValue extends Expr { }
|
||||
|
||||
private class ServletSetCookieValue extends SetCookieValue {
|
||||
ServletSetCookieValue() {
|
||||
exists(Call c |
|
||||
c.(ClassInstanceExpr).getConstructedType() instanceof TypeCookie and
|
||||
this = c.getArgument(1)
|
||||
or
|
||||
c.(MethodCall).getMethod() instanceof CookieSetValueMethod and
|
||||
this = c.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class NettySetCookieValue extends SetCookieValue {
|
||||
NettySetCookieValue() {
|
||||
exists(Call c |
|
||||
c.(ClassInstanceExpr).getConstructedType() instanceof NettyDefaultCookie and
|
||||
this = c.getArgument(1)
|
||||
or
|
||||
c.(MethodCall).getMethod() instanceof NettySetCookieValueMethod and
|
||||
this = c.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
/** Provides classes and predicates for reasoning about insecure randomness. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.frameworks.OpenSaml
|
||||
private import semmle.code.java.frameworks.Servlets
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.security.Cookies
|
||||
private import semmle.code.java.security.RandomQuery
|
||||
private import semmle.code.java.security.SensitiveActions
|
||||
private import semmle.code.java.security.SensitiveApi
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.security.RandomQuery
|
||||
|
||||
/**
|
||||
* A node representing a source of insecure randomness.
|
||||
@@ -18,7 +20,7 @@ abstract class InsecureRandomnessSource extends DataFlow::Node { }
|
||||
private class RandomMethodSource extends InsecureRandomnessSource {
|
||||
RandomMethodSource() {
|
||||
exists(RandomDataSource s | this.asExpr() = s.getOutput() |
|
||||
not s.getQualifier().getType() instanceof SafeRandomImplementation
|
||||
not s.getSourceOfRandomness() instanceof SafeRandomImplementation
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -40,7 +42,7 @@ private class TypeHadoopOsSecureRandom extends SafeRandomImplementation {
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing an operation which should not use a Insecurely random value.
|
||||
* A node representing an operation which should not use an insecurely random value.
|
||||
*/
|
||||
abstract class InsecureRandomnessSink extends DataFlow::Node { }
|
||||
|
||||
@@ -48,16 +50,7 @@ abstract class InsecureRandomnessSink extends DataFlow::Node { }
|
||||
* A node which sets the value of a cookie.
|
||||
*/
|
||||
private class CookieSink extends InsecureRandomnessSink {
|
||||
CookieSink() {
|
||||
exists(Call c |
|
||||
c.(ClassInstanceExpr).getConstructedType() instanceof TypeCookie and
|
||||
this.asExpr() = c.getArgument(1)
|
||||
or
|
||||
c.(MethodCall).getMethod().getDeclaringType() instanceof TypeCookie and
|
||||
c.(MethodCall).getMethod().hasName("setValue") and
|
||||
this.asExpr() = c.getArgument(0)
|
||||
)
|
||||
}
|
||||
CookieSink() { this.asExpr() instanceof SetCookieValue }
|
||||
}
|
||||
|
||||
private class SensitiveActionSink extends InsecureRandomnessSink {
|
||||
@@ -76,6 +69,8 @@ module InsecureRandomnessConfig implements DataFlow::ConfigSig {
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node n) { isSource(n) }
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node n) { isSink(n) }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
n1.asExpr() = n2.asExpr().(BinaryExpr).getAnOperand()
|
||||
or
|
||||
@@ -88,6 +83,17 @@ module InsecureRandomnessConfig implements DataFlow::ConfigSig {
|
||||
n1.asExpr() = mc.getArgument(0) and
|
||||
n2.asExpr() = mc
|
||||
)
|
||||
or
|
||||
// TODO: Once we have a default sanitizer for UUIDs, we can convert these to global summaries.
|
||||
exists(Call c |
|
||||
c.(ClassInstanceExpr).getConstructedType().hasQualifiedName("java.util", "UUID") and
|
||||
n1.asExpr() = c.getAnArgument() and
|
||||
n2.asExpr() = c
|
||||
or
|
||||
c.(MethodCall).getMethod().hasQualifiedName("java.util", "UUID", "toString") and
|
||||
n1.asExpr() = c.getQualifier() and
|
||||
n2.asExpr() = c
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.TypeFlow
|
||||
|
||||
/**
|
||||
* A method access that returns random data or writes random data to an argument.
|
||||
@@ -43,6 +44,9 @@ abstract class RandomDataSource extends MethodCall {
|
||||
* in the case where it writes random data to that argument.
|
||||
*/
|
||||
abstract Expr getOutput();
|
||||
|
||||
/** Gets the type of the source of randomness used by this call. */
|
||||
RefType getSourceOfRandomness() { boundOrStaticType(this.getQualifier(), result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,4 +171,18 @@ class ApacheCommonsRandomStringSource extends RandomDataSource {
|
||||
}
|
||||
|
||||
override Expr getOutput() { result = this }
|
||||
|
||||
override RefType getSourceOfRandomness() {
|
||||
if
|
||||
this.getMethod().hasStringSignature("random(int, int, int, boolean, boolean, char[], Random)")
|
||||
then boundOrStaticType(this.getArgument(6), result)
|
||||
else result.hasQualifiedName("java.util", "Random")
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `t` is the static type of `e`, or an upper bound of the runtime type of `e`. */
|
||||
private predicate boundOrStaticType(Expr e, RefType t) {
|
||||
exprTypeFlow(e, t, false)
|
||||
or
|
||||
t = e.getType()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/** Modules to reason about the tainting of environment variables */
|
||||
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.Maps
|
||||
private import semmle.code.java.JDK
|
||||
|
||||
private module ProcessBuilderEnvironmentConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(MethodCall mc | mc = source.asExpr() |
|
||||
mc.getMethod().hasQualifiedName("java.lang", "ProcessBuilder", "environment")
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(MapMutation mm).getQualifier() }
|
||||
}
|
||||
|
||||
private module ProcessBuilderEnvironmentFlow = DataFlow::Global<ProcessBuilderEnvironmentConfig>;
|
||||
|
||||
/**
|
||||
* A node that acts as a sanitizer in configurations related to environment variable injection.
|
||||
*/
|
||||
abstract class ExecTaintedEnvironmentSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration that tracks flow from unvalidated data to an environment variable for a subprocess.
|
||||
*/
|
||||
module ExecTaintedEnvironmentConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof ThreatModelFlowSource }
|
||||
|
||||
predicate isBarrier(DataFlow::Node barrier) { barrier instanceof ExecTaintedEnvironmentSanitizer }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sinkNode(sink, "environment-injection")
|
||||
or
|
||||
// sink is a key or value added to a `ProcessBuilder::environment` map.
|
||||
exists(MapMutation mm | mm.getAnArgument() = sink.asExpr() |
|
||||
ProcessBuilderEnvironmentFlow::flowToExpr(mm.getQualifier())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint-tracking flow for unvalidated data to an environment variable for a subprocess.
|
||||
*/
|
||||
module ExecTaintedEnvironmentFlow = TaintTracking::Global<ExecTaintedEnvironmentConfig>;
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Passing unvalidated user input into the environment variables of a subprocess can allow an attacker to execute malicious code.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>If possible, use hard-coded string literals to specify the environment variable or its value.
|
||||
Instead of passing the user input directly to the
|
||||
process or library function, examine the user input and then choose
|
||||
among hard-coded string literals.</p>
|
||||
|
||||
<p>If the applicable environment variables cannot be determined at
|
||||
compile time, then add code to verify that the user input string is
|
||||
safe before using it.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following (BAD) example, the environment variable <code>PATH</code> is set to the value of the user input <code>path</code> without validation.</p>
|
||||
|
||||
<sample src="ExecTaintedEnvironmentValue.java" />
|
||||
|
||||
<p>In the following (BAD) example, an environment variable is set with a name that is derived from the user input <code>var</code> without validation.</p>
|
||||
|
||||
<sample src="ExecTaintedEnvironmentName.java" />
|
||||
<p>In the following (GOOD) example, the user's input is validated before being used to set the environment variable.</p>
|
||||
|
||||
<sample src="ExecTaintedEnvironmentValidated.java" />
|
||||
|
||||
<p>In the following (GOOD) example, the user's input is checked and used to determine an environment variable to add.</p>
|
||||
|
||||
<sample src="ExecTaintedEnvironmentChecked.java" />
|
||||
</example>
|
||||
<references>
|
||||
<li>
|
||||
The Java Tutorials: <a href="https://docs.oracle.com/javase/tutorial/essential/environment/env.html">Environment Variables</a>.
|
||||
</li>
|
||||
<li>
|
||||
OWASP: <a href="https://owasp.org/www-community/attacks/Command_Injection">Command injection</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
24
java/ql/src/Security/CWE/CWE-078/ExecTaintedEnvironment.ql
Normal file
24
java/ql/src/Security/CWE/CWE-078/ExecTaintedEnvironment.ql
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @name Building a command with an injected environment variable
|
||||
* @description Passing environment variables containing externally controlled
|
||||
* strings to a command line is vulnerable to malicious changes to the
|
||||
* environment of a subprocess.
|
||||
* @problem.severity error
|
||||
* @kind path-problem
|
||||
* @security-severity 9.8
|
||||
* @precision medium
|
||||
* @id java/exec-tainted-environment
|
||||
* @tags security
|
||||
* external/cwe/cwe-078
|
||||
* external/cwe/cwe-088
|
||||
* external/cwe/cwe-454
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.security.TaintedEnvironmentVariableQuery
|
||||
import ExecTaintedEnvironmentFlow::PathGraph
|
||||
|
||||
from ExecTaintedEnvironmentFlow::PathNode source, ExecTaintedEnvironmentFlow::PathNode sink
|
||||
where ExecTaintedEnvironmentFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
"This command will be execute with a tainted environment variable."
|
||||
@@ -0,0 +1,7 @@
|
||||
Map<String, String> env = builder.environment();
|
||||
String debug = request.getParameter("debug");
|
||||
|
||||
// GOOD: Checking the value and not tainting the variable added to the environment
|
||||
if (debug != null) {
|
||||
env.put("PYTHONDEBUG", "1");
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response) {
|
||||
String attr = request.getParameter("attribute");
|
||||
String value = request.getParameter("value");
|
||||
|
||||
Map<String, String> env = processBuilder.environment();
|
||||
// BAD: attr and value are tainted and being added to the environment
|
||||
env.put(attr, value);
|
||||
|
||||
processBuilder.start();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
String opt = request.getParameter("opt");
|
||||
String value = request.getParameter("value");
|
||||
|
||||
Map<String, String> env = processBuilder.environment();
|
||||
|
||||
// GOOD: opt and value are checked before being added to the environment
|
||||
if (permittedJavaOptions.contains(opt) && validOption(opt, value)) {
|
||||
env.put(opt, value);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response) {
|
||||
String path = request.getParameter("path");
|
||||
|
||||
Map<String, String> env = processBuilder.environment();
|
||||
// BAD: path is tainted and being added to the environment
|
||||
env.put("PATH", path);
|
||||
|
||||
processBuilder.start();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added the `java/exec-tainted-environment` query, to detect the injection of environment variables names or values from remote input.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
| Test.kt:3:13:3:13 | List<Integer> l |
|
||||
| Test.kt:3:9:3:31 | List<Integer> l |
|
||||
| Test.kt:4:28:4:32 | index |
|
||||
| Test.kt:4:35:4:35 | p1 |
|
||||
| Test.kt:6:13:6:13 | Pair<Integer,Integer> p |
|
||||
| Test.kt:6:9:6:26 | Pair<Integer,Integer> p |
|
||||
| Test.kt:7:9:7:26 | Pair<Integer,Integer> <destruct> |
|
||||
| Test.kt:7:14:7:18 | int first |
|
||||
| Test.kt:7:26:7:26 | Pair<Integer,Integer> tmp0_container |
|
||||
| Test.kt:8:14:8:25 | Exception _ |
|
||||
| Test.kt:8:14:8:25 | Exception <unused var> |
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
| TaintedEnvironment.java:39:35:39:55 | new String[] | Command with a relative path 'ls' is executed. |
|
||||
| Test.java:50:46:50:49 | "ls" | Command with a relative path 'ls' is executed. |
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.security.TaintedEnvironmentVariableQuery
|
||||
import TestUtilities.InlineFlowTest
|
||||
|
||||
private class TestSource extends RemoteFlowSource {
|
||||
TestSource() { this.asExpr().(MethodCall).getMethod().hasName("source") }
|
||||
|
||||
override string getSourceType() { result = "test source" }
|
||||
}
|
||||
|
||||
import TaintFlowTest<ExecTaintedEnvironmentConfig>
|
||||
@@ -0,0 +1,41 @@
|
||||
import java.lang.ProcessBuilder;
|
||||
import java.lang.Runtime;
|
||||
import java.util.Map;
|
||||
|
||||
public class TaintedEnvironment {
|
||||
public Object source() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void buildProcess() throws java.io.IOException {
|
||||
String s = (String) source();
|
||||
ProcessBuilder pb = new ProcessBuilder();
|
||||
|
||||
pb.environment().put("foo", s); // $hasTaintFlow
|
||||
|
||||
pb.environment().put(s, "foo"); // $hasTaintFlow
|
||||
|
||||
Map<String, String> extra = Map.of("USER", s);
|
||||
|
||||
pb.environment().putAll(extra); // $hasTaintFlow
|
||||
|
||||
pb.environment().putIfAbsent("foo", s); // $hasTaintFlow
|
||||
pb.environment().putIfAbsent(s, "foo"); // $hasTaintFlow
|
||||
|
||||
pb.environment().replace("foo", s); // $hasTaintFlow
|
||||
pb.environment().replace(s, "foo"); // $hasTaintFlow
|
||||
pb.environment().replace("foo", "bar", s); // $hasTaintFlow
|
||||
|
||||
Map<String, String> env = pb.environment();
|
||||
|
||||
env.put("foo", s); // $hasTaintFlow
|
||||
|
||||
pb.start();
|
||||
}
|
||||
|
||||
public void exec() throws java.io.IOException {
|
||||
String kv = (String) source();
|
||||
|
||||
Runtime.getRuntime().exec(new String[] { "ls" }, new String[] { kv }); // $hasTaintFlow
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import javax.servlet.http.Cookie;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.owasp.esapi.Encoder;
|
||||
|
||||
public class WeakRandomCookies extends HttpServlet {
|
||||
public class InsecureRandomCookies extends HttpServlet {
|
||||
HttpServletResponse response;
|
||||
|
||||
public void doGet() {
|
||||
@@ -19,6 +19,14 @@ public class WeakRandomCookies extends HttpServlet {
|
||||
int c = r.nextInt();
|
||||
// BAD: The cookie value may be predictable.
|
||||
Cookie cookie = new Cookie("name", Integer.toString(c)); // $hasWeakRandomFlow
|
||||
cookie.setValue(Integer.toString(c)); // $hasWeakRandomFlow
|
||||
|
||||
io.netty.handler.codec.http.Cookie nettyCookie =
|
||||
new io.netty.handler.codec.http.DefaultCookie("name", Integer.toString(c)); // $hasWeakRandomFlow
|
||||
nettyCookie.setValue(Integer.toString(c)); // $hasWeakRandomFlow
|
||||
io.netty.handler.codec.http.cookie.Cookie nettyCookie2 =
|
||||
new io.netty.handler.codec.http.cookie.DefaultCookie("name", Integer.toString(c)); // $hasWeakRandomFlow
|
||||
nettyCookie2.setValue(Integer.toString(c)); // $hasWeakRandomFlow
|
||||
|
||||
Encoder enc = null;
|
||||
int c2 = r.nextInt();
|
||||
@@ -36,8 +44,8 @@ public class WeakRandomCookies extends HttpServlet {
|
||||
byte[] bytes2 = new byte[16];
|
||||
sr.nextBytes(bytes2);
|
||||
// GOOD: The cookie value is unpredictable.
|
||||
Cookie cookie4 = new Cookie("name", new String(bytes2));
|
||||
|
||||
Cookie cookie4 = new Cookie("name", new String(bytes2));
|
||||
|
||||
ThreadLocalRandom tlr = ThreadLocalRandom.current();
|
||||
|
||||
Cookie cookie5 = new Cookie("name", Integer.toString(tlr.nextInt())); // $hasWeakRandomFlow
|
||||
@@ -1 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/apache-commons-lang3-3.7:${testdir}/../../../stubs/esapi-2.0.1
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/apache-commons-lang3-3.7:${testdir}/../../../stubs/esapi-2.0.1:${testdir}/../../../stubs/netty-4.1.x
|
||||
|
||||
6
java/ql/test/stubs/netty-4.1.x/io/netty/handler/codec/http/Cookie.java
generated
Normal file
6
java/ql/test/stubs/netty-4.1.x/io/netty/handler/codec/http/Cookie.java
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
// Generated automatically from io.netty.handler.codec.http.cookie.Cookie for testing purposes
|
||||
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
public interface Cookie extends io.netty.handler.codec.http.cookie.Cookie {
|
||||
}
|
||||
9
java/ql/test/stubs/netty-4.1.x/io/netty/handler/codec/http/DefaultCookie.java
generated
Normal file
9
java/ql/test/stubs/netty-4.1.x/io/netty/handler/codec/http/DefaultCookie.java
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
// Generated automatically from io.netty.handler.codec.http.cookie.DefaultCookie for testing
|
||||
// purposes
|
||||
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
public class DefaultCookie extends io.netty.handler.codec.http.cookie.DefaultCookie
|
||||
implements Cookie {
|
||||
public DefaultCookie(String p0, String p1) {}
|
||||
}
|
||||
Reference in New Issue
Block a user