Compare commits

..

1 Commits

Author SHA1 Message Date
CodeQL CI
375a4f3c69 Update README.md 2023-05-03 17:50:41 +02:00
7654 changed files with 327115 additions and 560582 deletions

View File

@@ -1,9 +1,3 @@
common --enable_platform_specific_config
build --repo_env=CC=clang --repo_env=CXX=clang++
build:linux --cxxopt=-std=c++20
build:macos --cxxopt=-std=c++20 --cpu=darwin_x86_64
build:windows --cxxopt=/std:c++20 --cxxopt=/Zc:preprocessor
build --repo_env=CC=clang --repo_env=CXX=clang++ --cxxopt="-std=c++17"
try-import %workspace%/local.bazelrc

View File

@@ -1 +1 @@
6.3.1
6.1.2

View File

@@ -1,6 +1,6 @@
{
"extensions": [
"rust-lang.rust-analyzer",
"rust-lang.rust",
"bungcip.better-toml",
"github.vscode-codeql",
"hbenl.vscode-test-explorer",

View File

@@ -17,26 +17,3 @@ updates:
ignore:
- dependency-name: '*'
update-types: ['version-update:semver-patch', 'version-update:semver-minor']
- package-ecosystem: "gomod"
directory: "go/extractor"
schedule:
interval: "daily"
allow:
- dependency-name: "golang.org/x/mod"
- dependency-name: "golang.org/x/tools"
groups:
extractor-dependencies:
patterns:
- "golang.org/x/*"
reviewers:
- "github/codeql-go"
- package-ecosystem: "gomod"
directory: "go/ql/test"
schedule:
interval: "monthly"
ignore:
- dependency-name: "*"
reviewers:
- "github/codeql-go"

9
.github/labeler.yml vendored
View File

@@ -11,7 +11,7 @@ Go:
- change-notes/**/*go.*
Java:
- any: [ 'java/**/*', '!java/kotlin-extractor/**/*', '!java/ql/test/kotlin/**/*' ]
- any: [ 'java/**/*', '!java/kotlin-extractor/**/*', '!java/kotlin-explorer/**/*', '!java/ql/test/kotlin/**/*' ]
- change-notes/**/*java.*
JS:
@@ -20,6 +20,7 @@ JS:
Kotlin:
- java/kotlin-extractor/**/*
- java/kotlin-explorer/**/*
- java/ql/test/kotlin/**/*
Python:
@@ -45,7 +46,11 @@ documentation:
# Since these are all shared files that need to be synced, just pick _one_ copy of each.
"DataFlow Library":
- "shared/dataflow/**/*"
- "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll"
- "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll"
- "java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
- "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll"
- "java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll"
"ATM":
- javascript/ql/experimental/adaptivethreatmodeling/**/*

View File

@@ -9,42 +9,27 @@ on:
- "*/ql/lib/**/*.ql"
- "*/ql/lib/**/*.qll"
- "*/ql/lib/**/*.yml"
- "shared/**/*.ql"
- "shared/**/*.qll"
- "!**/experimental/**"
- "!ql/**"
- "!swift/**"
- ".github/workflows/check-change-note.yml"
jobs:
check-change-note:
env:
REPO: ${{ github.repository }}
PULL_REQUEST_NUMBER: ${{ github.event.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: ubuntu-latest
steps:
- name: Fail if no change note found. To fix, either add one, or add the `no-change-note-required` label.
if: |
github.event.pull_request.draft == false &&
!contains(github.event.pull_request.labels.*.name, 'no-change-note-required')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
change_note_files=$(gh api "repos/$REPO/pulls/$PULL_REQUEST_NUMBER/files" --paginate --jq '.[].filename | select(test("/change-notes/.*[.]md$"))')
if [ -z "$change_note_files" ]; then
echo "No change note found. Either add one, or add the 'no-change-note-required' label."
exit 1
fi
echo "Change notes found:"
echo "$change_note_files"
- name: Fail if the change note filename doesn't match the expected format. The file name must be of the form 'YYYY-MM-DD.md', 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text, or released/x.y.z.md for released change-notes
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' |
grep true -c
- name: Fail if the change note filename doesn't match the expected format. The file name must be of the form 'YYYY-MM-DD.md' or 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text.
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
bad_change_note_file_names=$(gh api "repos/$REPO/pulls/$PULL_REQUEST_NUMBER/files" --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))][] | select((test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$") or test("/change-notes/released/[0-9]*[.][0-9]*[.][0-9]*[.]md$")) | not)')
if [ -n "$bad_change_note_file_names" ]; then
echo "The following change note file names are invalid:"
echo "$bad_change_note_file_names"
exit 1
fi
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))] | all(test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$"))' |
grep true -c

View File

@@ -1,29 +0,0 @@
name: "Check implicit this warnings"
on:
workflow_dispatch:
pull_request:
paths:
- "**qlpack.yml"
branches:
- main
- "rc/*"
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check that implicit this warnings is enabled for all packs
shell: bash
run: |
EXIT_CODE=0
packs="$(find . -iname 'qlpack.yml')"
for pack_file in ${packs}; do
option="$(yq '.warnOnImplicitThis' ${pack_file})"
if [ "${option}" != "true" ]; then
echo "::error file=${pack_file}::warnOnImplicitThis property must be set to 'true' for pack ${pack_file}"
EXIT_CODE=1
fi
done
exit "${EXIT_CODE}"

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 2

View File

@@ -16,6 +16,6 @@ jobs:
name: Check query IDs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Check for duplicate query IDs
run: python3 misc/scripts/check-query-ids.py

View File

@@ -33,7 +33,7 @@ jobs:
dotnet-version: 7.0.102
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Setup CodeQL
uses: ./.github/actions/fetch-codeql
with:
@@ -29,9 +29,9 @@ jobs:
# run with --check-only if running in a PR (github.sha != main)
if : ${{ github.event_name == 'pull_request' }}
shell: bash
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
- name: compile queries - full
# do full compile if running on main - this populates the cache
if : ${{ github.event_name != 'pull_request' }}
shell: bash
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" --compilation-cache-size=500
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"

View File

@@ -29,7 +29,7 @@ jobs:
qlupgrade:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- name: Check DB upgrade scripts
run: |
@@ -52,7 +52,8 @@ jobs:
matrix:
slice: ["1/2", "2/2"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- uses: ./csharp/actions/create-extractor-pack
- name: Cache compilation cache
id: query-cache
@@ -61,41 +62,25 @@ jobs:
key: csharp-qltest-${{ matrix.slice }}
- name: Run QL tests
run: |
codeql test run --threads=0 --ram 50000 --slice ${{ matrix.slice }} --search-path extractor-pack --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
CODEQL_PATH=$(gh codeql version --format=json | jq -r .unpackedLocation)
# The legacy ASP extractor is not in this repo, so take the one from the nightly build
mv "$CODEQL_PATH/csharp/tools/extractor-asp.jar" "${{ github.workspace }}/csharp/extractor-pack/tools"
# Safe guard against using the bundled extractor
rm -rf "$CODEQL_PATH/csharp"
codeql test run --threads=0 --ram 50000 --slice ${{ matrix.slice }} --search-path "${{ github.workspace }}/csharp/extractor-pack" --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
GITHUB_TOKEN: ${{ github.token }}
unit-tests:
strategy:
matrix:
os: [ubuntu-latest, windows-2019]
runs-on: ${{ matrix.os }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Setup dotnet
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.102
- name: Extractor unit tests
run: |
dotnet test -p:RuntimeFrameworkVersion=7.0.2 extractor/Semmle.Util.Tests
dotnet test -p:RuntimeFrameworkVersion=7.0.2 extractor/Semmle.Extraction.Tests
dotnet test -p:RuntimeFrameworkVersion=7.0.2 autobuilder/Semmle.Autobuild.CSharp.Tests
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/extractor/Semmle.Util.Tests"
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/extractor/Semmle.Extraction.Tests"
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests"
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
shell: bash
stubgentest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./csharp/actions/create-extractor-pack
- name: Run stub generator tests
run: |
# Generate (Asp)NetCore stubs
STUBS_PATH=stubs_output
python3 scripts/stubs/make_stubs_nuget.py webapp Swashbuckle.AspNetCore.Swagger 6.5.0 "$STUBS_PATH"
rm -rf ql/test/resources/stubs/_frameworks
# Update existing stubs in the repo with the freshly generated ones
mv "$STUBS_PATH/output/stubs/_frameworks" ql/test/resources/stubs/
git status
codeql test run --threads=0 --search-path extractor-pack --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries -- ql/test/library-tests/dataflow/flowsources/aspremote
env:
GITHUB_TOKEN: ${{ github.token }}

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup CodeQL
uses: ./.github/actions/fetch-codeql
- name: Create empty database
@@ -47,7 +47,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup CodeQL
uses: ./.github/actions/fetch-codeql
- name: Create empty database

View File

@@ -10,7 +10,6 @@ on:
- "*/ql/src/**/*.qll"
- "*/ql/lib/**/*.ql"
- "*/ql/lib/**/*.qll"
- "*/ql/lib/ext/**/*.yml"
- "misc/scripts/library-coverage/*.py"
# input data files
- "*/documentation/library-coverage/cwe-sink.csv"
@@ -31,11 +30,11 @@ jobs:
GITHUB_CONTEXT: ${{ toJSON(github.event) }}
run: echo "$GITHUB_CONTEXT"
- name: Clone self (github/codeql) - MERGE
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
path: merge
- name: Clone self (github/codeql) - BASE
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
fetch-depth: 2
path: base
@@ -89,32 +88,9 @@ jobs:
- name: Save PR number
run: |
mkdir -p pr
echo ${PR_NUMBER} > pr/NR
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
echo ${{ github.event.pull_request.number }} > pr/NR
- name: Upload PR number
uses: actions/upload-artifact@v3
with:
name: pr
path: pr/
- name: Save comment ID (if it exists)
run: |
# Find the latest comment starting with COMMENT_PREFIX
COMMENT_PREFIX=":warning: The head of this PR and the base branch were compared for differences in the framework coverage reports."
COMMENT_ID=$(gh api "repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments" --paginate | jq --arg prefix "${COMMENT_PREFIX}" 'map(select(.body|startswith($prefix)) | .id) | max // empty')
if [[ -z ${COMMENT_ID} ]]
then
echo "Comment not found. Not uploading 'comment/ID' artifact."
else
mkdir -p comment
echo ${COMMENT_ID} > comment/ID
fi
env:
GITHUB_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
- name: Upload comment ID (if it exists)
uses: actions/upload-artifact@v3
with:
name: comment
path: comment/
if-no-files-found: ignore

View File

@@ -20,7 +20,7 @@ jobs:
GITHUB_CONTEXT: ${{ toJSON(github.event) }}
run: echo "$GITHUB_CONTEXT"
- name: Clone self (github/codeql)
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Set up Python 3.8
uses: actions/setup-python@v4
with:

View File

@@ -9,11 +9,11 @@ jobs:
steps:
- name: Clone self (github/codeql)
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
path: script
- name: Clone self (github/codeql) for analysis
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
path: codeqlModels
fetch-depth: 0

View File

@@ -17,7 +17,7 @@ jobs:
GITHUB_CONTEXT: ${{ toJSON(github.event) }}
run: echo "$GITHUB_CONTEXT"
- name: Clone self (github/codeql)
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
path: ql
fetch-depth: 0

View File

@@ -13,11 +13,11 @@ jobs:
steps:
- name: Clone self (github/codeql)
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
path: script
- name: Clone self (github/codeql) for analysis
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
path: codeqlModels
ref: ${{ github.event.inputs.qlModelShaOverride || github.ref }}

View File

@@ -25,7 +25,7 @@ jobs:
exit 1
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Git config
shell: bash

View File

@@ -7,21 +7,19 @@ on:
- .github/workflows/go-tests-other-os.yml
- .github/actions/**
- codeql-workspace.yml
env:
GO_VERSION: '~1.21.0'
jobs:
test-mac:
name: Test MacOS
runs-on: macos-latest
steps:
- name: Set up Go ${{ env.GO_VERSION }}
- name: Set up Go 1.20
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
go-version: '1.20'
id: go
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
@@ -49,14 +47,14 @@ jobs:
name: Test Windows
runs-on: windows-latest-xl
steps:
- name: Set up Go ${{ env.GO_VERSION }}
- name: Set up Go 1.20
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
go-version: '1.20'
id: go
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql

View File

@@ -15,21 +15,19 @@ on:
- .github/workflows/go-tests.yml
- .github/actions/**
- codeql-workspace.yml
env:
GO_VERSION: '~1.21.0'
jobs:
test-linux:
name: Test Linux (Ubuntu)
runs-on: ubuntu-latest-xl
steps:
- name: Set up Go ${{ env.GO_VERSION }}
- name: Set up Go 1.20
uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
go-version: '1.20'
id: go
- name: Check out code
uses: actions/checkout@v4
uses: actions/checkout@v2
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql

65
.github/workflows/js-ml-tests.yml vendored Normal file
View File

@@ -0,0 +1,65 @@
name: JS ML-powered queries tests
on:
push:
paths:
- "javascript/ql/experimental/adaptivethreatmodeling/**"
- .github/workflows/js-ml-tests.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
- "rc/*"
pull_request:
paths:
- "javascript/ql/experimental/adaptivethreatmodeling/**"
- .github/workflows/js-ml-tests.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
workflow_dispatch:
defaults:
run:
working-directory: javascript/ql/experimental/adaptivethreatmodeling
jobs:
qltest:
name: Test QL
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- name: Install pack dependencies
run: |
for pack in modelbuilding src test; do
codeql pack install --mode verify -- "${pack}"
done
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: js-ml-test
- name: Check QL compilation
run: |
codeql query compile \
--check-only \
--ram 50000 \
--additional-packs "${{ github.workspace }}" \
--threads=0 \
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
-- \
lib modelbuilding src
- name: Run QL tests
run: |
codeql test run \
--threads=0 \
--ram 50000 \
--additional-packs "${{ github.workspace }}" \
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
-- \
test

View File

@@ -27,12 +27,12 @@ jobs:
slug: ${{fromJson(github.event.inputs.projects || '["apache/commons-codec", "apache/commons-io", "apache/commons-beanutils", "apache/commons-logging", "apache/commons-fileupload", "apache/commons-lang", "apache/commons-validator", "apache/commons-csv", "apache/dubbo"]' )}}
steps:
- name: Clone github/codeql from PR
uses: actions/checkout@v4
uses: actions/checkout@v3
if: github.event.pull_request
with:
path: codeql-pr
- name: Clone github/codeql from main
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
path: codeql-main
ref: main

View File

@@ -27,11 +27,11 @@ jobs:
ref: "placeholder"
steps:
- name: Clone self (github/codeql)
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup CodeQL binaries
uses: ./.github/actions/fetch-codeql
- name: Clone repositories
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
path: repos/${{ matrix.ref }}
ref: ${{ matrix.ref }}

View File

@@ -43,7 +43,7 @@ jobs:
if-no-files-found: error
retention-days: 1
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 2
persist-credentials: false

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest-xl
steps:
### Build the queries ###
- uses: actions/checkout@v4
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Find codeql
@@ -32,7 +32,7 @@ jobs:
path: |
ql/extractor-pack/
ql/target/release/buramu
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ql/**/*.rs') }}
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
- name: Cache cargo
if: steps.cache-extractor.outputs.cache-hit != 'true'
uses: actions/cache@v3

View File

@@ -21,7 +21,7 @@ jobs:
- github/codeql
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Find codeql
id: find-codeql
@@ -42,7 +42,7 @@ jobs:
env:
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
- name: Checkout ${{ matrix.repo }}
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
repository: ${{ matrix.repo }}
path: ${{ github.workspace }}/repo
@@ -71,7 +71,7 @@ jobs:
runs-on: ubuntu-latest
needs: measure
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: measurements

View File

@@ -21,7 +21,7 @@ jobs:
qltest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@v2
@@ -61,7 +61,7 @@ jobs:
needs: [qltest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Install GNU tar
if: runner.os == 'macOS'
run: |

View File

@@ -20,7 +20,7 @@ jobs:
steps:
- name: Clone self (github/codeql)
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
path: codeql
- name: Set up Python 3.8

View File

@@ -42,7 +42,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Install GNU tar
if: runner.os == 'macOS'
run: |
@@ -61,7 +61,7 @@ jobs:
ruby/extractor/target/release/codeql-extractor-ruby
ruby/extractor/target/release/codeql-extractor-ruby.exe
ruby/extractor/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ruby/extractor/**/*.rs') }}
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}--${{ hashFiles('ruby/extractor/**/*.rs') }}
- uses: actions/cache@v3
if: steps.cache-extractor.outputs.cache-hit != 'true'
with:
@@ -113,7 +113,7 @@ jobs:
compile-queries:
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Fetch CodeQL
uses: ./.github/actions/fetch-codeql
- name: Cache compilation cache
@@ -145,7 +145,7 @@ jobs:
runs-on: ubuntu-latest
needs: [build, compile-queries]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: ruby.dbscheme
@@ -206,7 +206,7 @@ jobs:
runs-on: ${{ matrix.os }}
needs: [package]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Fetch CodeQL
uses: ./.github/actions/fetch-codeql

View File

@@ -27,14 +27,14 @@ jobs:
repo: [rails/rails, discourse/discourse, spree/spree, ruby/ruby]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack
- name: Checkout ${{ matrix.repo }}
uses: actions/checkout@v4
uses: actions/checkout@v3
with:
repository: ${{ matrix.repo }}
path: ${{ github.workspace }}/repo
@@ -59,7 +59,7 @@ jobs:
runs-on: ubuntu-latest
needs: measure
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: measurements

View File

@@ -14,7 +14,6 @@ on:
pull_request:
paths:
- "ruby/**"
- "shared/**"
- .github/workflows/ruby-qltest.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
@@ -33,7 +32,7 @@ jobs:
qlupgrade:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- name: Check DB upgrade scripts
run: |
@@ -54,7 +53,7 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack
- name: Cache compilation cache

View File

@@ -16,7 +16,6 @@ on:
branches:
- main
- rc/*
- codeql-cli-*
push:
paths:
- "swift/**"
@@ -31,7 +30,6 @@ on:
branches:
- main
- rc/*
- codeql-cli-*
jobs:
# not using a matrix as you cannot depend on a specific job in a matrix, and we want to start linux checks
@@ -39,31 +37,31 @@ jobs:
build-and-test-macos:
runs-on: macos-12-xl
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./swift/actions/build-and-test
build-and-test-linux:
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./swift/actions/build-and-test
qltests-linux:
needs: build-and-test-linux
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./swift/actions/run-ql-tests
qltests-macos:
if : ${{ github.event_name == 'pull_request' }}
needs: build-and-test-macos
runs-on: macos-12-xl
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./swift/actions/run-ql-tests
integration-tests-linux:
needs: build-and-test-linux
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./swift/actions/run-integration-tests
integration-tests-macos:
if : ${{ github.event_name == 'pull_request' }}
@@ -71,13 +69,13 @@ jobs:
runs-on: macos-12-xl
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./swift/actions/run-integration-tests
codegen:
if : ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: bazelbuild/setup-bazelisk@v2
- uses: actions/setup-python@v4
with:
@@ -102,6 +100,6 @@ jobs:
if : ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- uses: ./swift/actions/database-upgrade-scripts

View File

@@ -14,9 +14,7 @@ jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Check synchronized files
run: python config/sync-files.py
- name: Check dbscheme fragments
run: python config/sync-dbscheme-fragments.py

View File

@@ -1,46 +0,0 @@
name: Test tree-sitter-extractor
on:
push:
paths:
- "shared/tree-sitter-extractor/**"
- .github/workflows/tree-sitter-extractor-test.yml
branches:
- main
- "rc/*"
pull_request:
paths:
- "shared/tree-sitter-extractor/**"
- .github/workflows/tree-sitter-extractor-test.yml
branches:
- main
- "rc/*"
env:
CARGO_TERM_COLOR: always
defaults:
run:
working-directory: shared/tree-sitter-extractor
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run tests
run: cargo test --verbose
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check formatting
run: cargo fmt --check
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run clippy
run: cargo clippy -- --no-deps -D warnings -A clippy::new_without_default -A clippy::too_many_arguments

View File

@@ -20,7 +20,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup CodeQL
uses: ./.github/actions/fetch-codeql

View File

@@ -5,9 +5,9 @@ repos:
rev: v3.2.0
hooks:
- id: trailing-whitespace
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)|.*\.patch
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
- id: end-of-file-fixer
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)|.*\.patch
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v13.0.1
@@ -21,11 +21,6 @@ repos:
- id: autopep8
files: ^misc/codegen/.*\.py
- repo: https://github.com/warchant/pre-commit-buildifier
rev: 0.0.2
hooks:
- id: buildifier
- repo: local
hooks:
- id: codeql-format

18
.vscode/tasks.json vendored
View File

@@ -22,22 +22,6 @@
"command": "${config:python.pythonPath}",
},
"problemMatcher": []
},
{
"label": "Accept .expected changes from CI",
"type": "process",
// Non-Windows OS will usually have Python 3 already installed at /usr/bin/python3.
"command": "python3",
"args": [
"misc/scripts/accept-expected-changes-from-ci.py"
],
"group": "build",
"windows": {
// On Windows, use whatever Python interpreter is configured for this workspace. The default is
// just `python`, so if Python is already on the path, this will find it.
"command": "${config:python.pythonPath}",
},
"problemMatcher": []
}
]
}
}

View File

@@ -8,6 +8,7 @@
/swift/ @github/codeql-swift
/misc/codegen/ @github/codeql-swift
/java/kotlin-extractor/ @github/codeql-kotlin
/java/kotlin-explorer/ @github/codeql-kotlin
# ML-powered queries
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
@@ -39,6 +40,3 @@ WORKSPACE.bazel @github/codeql-ci-reviewers
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
/.github/workflows/ruby-* @github/codeql-ruby
/.github/workflows/swift.yml @github/codeql-swift
# Misc
/misc/scripts/accept-expected-changes-from-ci.py @RasmusWL

View File

@@ -14,16 +14,14 @@ If you have an idea for a query that you would like to share with other CodeQL u
1. **Directory structure**
There are eight language-specific query directories in this repository:
There are six language-specific query directories in this repository:
* C/C++: `cpp/ql/src`
* C#: `csharp/ql/src`
* Go: `go/ql/src`
* Java/Kotlin: `java/ql/src`
* Java: `java/ql/src`
* JavaScript: `javascript/ql/src`
* Python: `python/ql/src`
* Ruby: `ruby/ql/src`
* Swift: `swift/ql/src`
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.

View File

@@ -1,3 +1,5 @@
# Do not integrate this change
# CodeQL
This open source repository contains the standard CodeQL libraries and queries that power [GitHub Advanced Security](https://github.com/features/security/code) and the other application security products that [GitHub](https://github.com/features/security/) makes available to its customers worldwide.

View File

@@ -4,9 +4,7 @@ provide:
- "*/ql/test/qlpack.yml"
- "*/ql/examples/qlpack.yml"
- "*/ql/consistency-queries/qlpack.yml"
- "*/ql/automodel/src/qlpack.yml"
- "*/ql/automodel/test/qlpack.yml"
- "shared/**/qlpack.yml"
- "shared/*/qlpack.yml"
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
- "go/ql/config/legacy-support/qlpack.yml"
- "go/build/codeql-extractor-go/codeql-extractor.yml"

View File

@@ -1,33 +0,0 @@
{
"files": [
"javascript/ql/lib/semmlecode.javascript.dbscheme",
"python/ql/lib/semmlecode.python.dbscheme",
"ruby/ql/lib/ruby.dbscheme",
"ql/ql/src/ql.dbscheme"
],
"fragments": [
"/*- External data -*/",
"/*- Files and folders -*/",
"/*- Diagnostic messages -*/",
"/*- Diagnostic messages: severity -*/",
"/*- Source location prefix -*/",
"/*- Lines of code -*/",
"/*- Configuration files with key value pairs -*/",
"/*- YAML -*/",
"/*- XML Files -*/",
"/*- XML: sourceline -*/",
"/*- DEPRECATED: External defects and metrics -*/",
"/*- DEPRECATED: Snapshot date -*/",
"/*- DEPRECATED: Duplicate code -*/",
"/*- DEPRECATED: Version control data -*/",
"/*- JavaScript-specific part -*/",
"/*- Ruby dbscheme -*/",
"/*- Erb dbscheme -*/",
"/*- QL dbscheme -*/",
"/*- Dbscheme dbscheme -*/",
"/*- Yaml dbscheme -*/",
"/*- Blame dbscheme -*/",
"/*- JSON dbscheme -*/",
"/*- Python dbscheme -*/"
]
}

View File

@@ -1,4 +1,24 @@
{
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlow.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlow.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlow.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlow.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlow.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlow.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlow.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlow.qll"
],
"DataFlowImpl Java/C++/C#/Go/Python/Ruby/Swift": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll"
],
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift Legacy Configuration": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll",
@@ -22,14 +42,38 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplForRegExp.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll"
],
"DataFlow Java/C++/C#/Go/Python/Ruby/Swift Common": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplCommon.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplCommon.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplCommon.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplCommon.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplCommon.qll"
],
"TaintTracking Java/C++/C#/Go/Python/Ruby/Swift": [
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTracking.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTracking.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTracking.qll",
"go/ql/lib/semmle/go/dataflow/internal/tainttracking1/TaintTracking.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTracking.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking1/TaintTracking.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTracking.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTracking.qll"
],
"TaintTracking Legacy Configuration Java/C++/C#/Go/Python/Ruby/Swift": [
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll",
@@ -53,6 +97,15 @@
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
],
"DataFlow Java/C++/C#/Python/Ruby/Swift Consistency checks": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll"
],
"DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
@@ -459,17 +512,17 @@
"SensitiveDataHeuristics Python/JS": [
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll",
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll",
"swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll"
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll"
],
"CFG": [
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
"ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll",
"swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll"
],
"TypeTracker": [
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
],
"SummaryTypeTracker": [
"python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
],
"AccessPathSyntax": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
@@ -498,6 +551,22 @@
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsExtensions.qll",
"python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModelsExtensions.qll"
],
"TaintedFormatStringQuery Ruby/JS": [
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringQuery.qll",
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringQuery.qll"
],
"TaintedFormatStringCustomizations Ruby/JS": [
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringCustomizations.qll",
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringCustomizations.qll"
],
"HttpToFileAccessQuery JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessQuery.qll",
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessQuery.qll"
],
"HttpToFileAccessCustomizations JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessCustomizations.qll",
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessCustomizations.qll"
],
"Typo database": [
"javascript/ql/src/Expressions/TypoDatabase.qll",
"ql/ql/src/codeql_ql/style/TypoDatabase.qll"
@@ -529,9 +598,5 @@
"EncryptionKeySizes Python/Java": [
"python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
],
"Python model summaries test extension": [
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
]
}
}

View File

@@ -1,86 +0,0 @@
#!/usr/bin/env python3
import argparse
import json
import os
import pathlib
import re
def make_groups(blocks):
groups = {}
for block in blocks:
groups.setdefault("".join(block["lines"]), []).append(block)
return list(groups.values())
def validate_fragments(fragments):
ok = True
for header, blocks in fragments.items():
groups = make_groups(blocks)
if len(groups) > 1:
ok = False
print("Warning: dbscheme fragments with header '{}' are different for {}".format(header, ["{}:{}:{}".format(
group[0]["file"], group[0]["start"], group[0]["end"]) for group in groups]))
return ok
def main():
script_path = os.path.realpath(__file__)
script_dir = os.path.dirname(script_path)
parser = argparse.ArgumentParser(
prog=os.path.basename(script_path),
description='Sync dbscheme fragments across files.'
)
parser.add_argument('files', metavar='dbscheme_file', type=pathlib.Path, nargs='*', default=[],
help='dbscheme files to check')
args = parser.parse_args()
with open(os.path.join(script_dir, "dbscheme-fragments.json"), "r") as f:
config = json.load(f)
fragment_headers = set(config["fragments"])
fragments = {}
ok = True
for file in args.files + config["files"]:
with open(os.path.join(os.path.dirname(script_dir), file), "r") as dbscheme:
header = None
line_number = 1
block = {"file": file, "start": line_number,
"end": None, "lines": []}
def end_block():
block["end"] = line_number - 1
if len(block["lines"]) > 0:
if header is None:
if re.match(r'(?m)\A(\s|//.*$|/\*(\**[^\*])*\*+/)*\Z', "".join(block["lines"])):
# Ignore comments at the beginning of the file
pass
else:
ok = False
print("Warning: dbscheme fragment without header: {}:{}:{}".format(
block["file"], block["start"], block["end"]))
else:
fragments.setdefault(header, []).append(block)
for line in dbscheme:
m = re.match(r"^\/\*-.*-\*\/$", line)
if m:
end_block()
header = line.strip()
if header not in fragment_headers:
ok = False
print("Warning: unknown header for dbscheme fragment: '{}': {}:{}".format(
header, file, line_number))
block = {"file": file, "start": line_number,
"end": None, "lines": []}
block["lines"].append(line)
line_number += 1
block["lines"].append('\n')
line_number += 1
end_block()
if not ok or not validate_fragments(fragments):
exit(1)
if __name__ == "__main__":
main()

View File

@@ -1,17 +1,12 @@
load("@rules_pkg//:mappings.bzl", "pkg_filegroup")
package(default_visibility = ["//visibility:public"])
load("@rules_pkg//:mappings.bzl", "pkg_filegroup")
alias(
name = "dbscheme",
actual = "//cpp/ql/lib:dbscheme",
)
alias(
name = "dbscheme-stats",
actual = "//cpp/ql/lib:dbscheme-stats",
)
pkg_filegroup(
name = "db-files",
srcs = [

View File

@@ -327,7 +327,7 @@ namespace Semmle.Autobuild.Cpp.Tests
{
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1;
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program^ Files^ ^(x86^)\Microsoft^ Visual^ Studio^ 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0;
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;

View File

@@ -1,19 +0,0 @@
class BuiltinType extends @builtintype {
string toString() { none() }
}
from BuiltinType type, string name, int kind, int kind_new, int size, int sign, int alignment
where
builtintypes(type, name, kind, size, sign, alignment) and
if
type instanceof @fp16 or
type instanceof @std_bfloat16 or
type instanceof @std_float16 or
type instanceof @complex_std_float32 or
type instanceof @complex_float32x or
type instanceof @complex_std_float64 or
type instanceof @complex_float64x or
type instanceof @complex_std_float128
then kind_new = 2
else kind_new = kind
select type, name, kind_new, size, sign, alignment

View File

@@ -1,3 +0,0 @@
description: Introduce new floating-point types from C23 and C++23
compatibility: backwards
builtintypes.rel: run builtintypes.qlo

View File

@@ -1,2 +0,0 @@
description: Remove _Float128 type
compatibility: full

View File

@@ -1,2 +0,0 @@
description: Make __is_trivial a builtin operation
compatibility: full

View File

@@ -1,3 +0,0 @@
description: Introduce extractor version numbers
compatibility: breaking
extractor_version.rel: delete

View File

@@ -2,4 +2,3 @@ name: codeql/cpp-downgrades
groups: cpp
downgrades: .
library: true
warnOnImplicitThis: true

View File

@@ -4,4 +4,3 @@ groups:
- examples
dependencies:
codeql/cpp-all: ${workspace}
warnOnImplicitThis: true

View File

@@ -1,7 +1,7 @@
load("@rules_pkg//:mappings.bzl", "pkg_files")
package(default_visibility = ["//cpp:__pkg__"])
load("@rules_pkg//:mappings.bzl", "pkg_files")
pkg_files(
name = "dbscheme",
srcs = ["semmlecode.cpp.dbscheme"],

View File

@@ -1,131 +1,3 @@
## 0.11.0
### Breaking Changes
* The `Container` and `Folder` classes now derive from `ElementBase` instead of `Locatable`, and no longer expose the `getLocation` predicate. Use `getURL` instead.
### New Features
* Added a new class `AdditionalCallTarget` for specifying additional call targets.
### Minor Analysis Improvements
* More field accesses are identified as `ImplicitThisFieldAccess`.
* Added support for new floating-point types in C23 and C++23.
## 0.10.1
### Minor Analysis Improvements
* Deleted the deprecated `AnalysedString` class, use the new name `AnalyzedString`.
* Deleted the deprecated `isBarrierGuard` predicate from the dataflow library and its uses, use `isBarrier` and the `BarrierGuard` module instead.
## 0.10.0
### Minor Analysis Improvements
* Functions that do not return due to calling functions that don't return (e.g. `exit`) are now detected as
non-returning in the IR and dataflow.
* Treat functions that reach the end of the function as returning in the IR.
They used to be treated as unreachable but it is allowed in C.
* The `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.
## 0.9.3
No user-facing changes.
## 0.9.2
### Deprecated APIs
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.
### New Features
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`
### Minor Analysis Improvements
* `delete` and `delete[]` are now modeled as calls to the relevant `operator delete` in the IR. In the case of a dynamic delete call a new instruction `VirtualDeleteFunctionAddress` is used to represent a function that dispatches to the correct delete implementation.
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.
## 0.9.1
No user-facing changes.
## 0.9.0
### Breaking Changes
* The `shouldPrintFunction` predicate from `PrintAstConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
* The `shouldPrintFunction` predicate from `PrintIRConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
### Major Analysis Improvements
* The `PrintAST` library now also prints global and namespace variables and their initializers.
### Minor Analysis Improvements
* The `_Float128x` type is no longer exposed as a builtin type. As this type could not occur any code base, this should only affect queries that explicitly looked at the builtin types.
## 0.8.1
### Deprecated APIs
* The library `semmle.code.cpp.dataflow.DataFlow` has been deprecated. Please use `semmle.code.cpp.dataflow.new.DataFlow` instead.
### New Features
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Data flow configurations can now include a predicate `neverSkip(Node node)`
in order to ensure inclusion of certain nodes in the path explanations. The
predicate defaults to the end-points of the additional flow steps provided in
the configuration, which means that such steps now always are visible by
default in path explanations.
* The `IRGuards` library has improved handling of pointer addition and subtraction operations.
## 0.8.0
### New Features
* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.
## 0.7.4
No user-facing changes.
## 0.7.3
### Minor Analysis Improvements
* Deleted the deprecated `hasCopyConstructor` predicate from the `Class` class in `Class.qll`.
* Deleted many deprecated predicates and classes with uppercase `AST`, `SSA`, `CFG`, `API`, etc. in their names. Use the PascalCased versions instead.
* Deleted the deprecated `CodeDuplication.qll` file.
## 0.7.2
### New Features
* Added an AST-based interface (`semmle.code.cpp.rangeanalysis.new.RangeAnalysis`) for the relative range analysis library.
* A new predicate `BarrierGuard::getAnIndirectBarrierNode` has been added to the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow`) to mark indirect expressions as barrier nodes using the `BarrierGuard` API.
### Major Analysis Improvements
* In the intermediate representation, handling of control flow after non-returning calls has been improved. This should remove false positives in queries that use the intermedite representation or libraries based on it, including the new data flow library.
### Minor Analysis Improvements
* The `StdNamespace` class now also includes all inline namespaces that are children of `std` namespace.
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.
## 0.7.1
No user-facing changes.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.

View File

@@ -1,9 +0,0 @@
## 0.10.0
### Minor Analysis Improvements
* Functions that do not return due to calling functions that don't return (e.g. `exit`) are now detected as
non-returning in the IR and dataflow.
* Treat functions that reach the end of the function as returning in the IR.
They used to be treated as unreachable but it is allowed in C.
* The `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.

View File

@@ -1,6 +0,0 @@
## 0.10.1
### Minor Analysis Improvements
* Deleted the deprecated `AnalysedString` class, use the new name `AnalyzedString`.
* Deleted the deprecated `isBarrierGuard` predicate from the dataflow library and its uses, use `isBarrier` and the `BarrierGuard` module instead.

View File

@@ -1,14 +0,0 @@
## 0.11.0
### Breaking Changes
* The `Container` and `Folder` classes now derive from `ElementBase` instead of `Locatable`, and no longer expose the `getLocation` predicate. Use `getURL` instead.
### New Features
* Added a new class `AdditionalCallTarget` for specifying additional call targets.
### Minor Analysis Improvements
* More field accesses are identified as `ImplicitThisFieldAccess`.
* Added support for new floating-point types in C23 and C++23.

View File

@@ -1,15 +0,0 @@
## 0.7.2
### New Features
* Added an AST-based interface (`semmle.code.cpp.rangeanalysis.new.RangeAnalysis`) for the relative range analysis library.
* A new predicate `BarrierGuard::getAnIndirectBarrierNode` has been added to the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow`) to mark indirect expressions as barrier nodes using the `BarrierGuard` API.
### Major Analysis Improvements
* In the intermediate representation, handling of control flow after non-returning calls has been improved. This should remove false positives in queries that use the intermedite representation or libraries based on it, including the new data flow library.
### Minor Analysis Improvements
* The `StdNamespace` class now also includes all inline namespaces that are children of `std` namespace.
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.

View File

@@ -1,7 +0,0 @@
## 0.7.3
### Minor Analysis Improvements
* Deleted the deprecated `hasCopyConstructor` predicate from the `Class` class in `Class.qll`.
* Deleted many deprecated predicates and classes with uppercase `AST`, `SSA`, `CFG`, `API`, etc. in their names. Use the PascalCased versions instead.
* Deleted the deprecated `CodeDuplication.qll` file.

View File

@@ -1,3 +0,0 @@
## 0.7.4
No user-facing changes.

View File

@@ -1,9 +0,0 @@
## 0.8.0
### New Features
* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.

View File

@@ -1,19 +0,0 @@
## 0.8.1
### Deprecated APIs
* The library `semmle.code.cpp.dataflow.DataFlow` has been deprecated. Please use `semmle.code.cpp.dataflow.new.DataFlow` instead.
### New Features
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Data flow configurations can now include a predicate `neverSkip(Node node)`
in order to ensure inclusion of certain nodes in the path explanations. The
predicate defaults to the end-points of the additional flow steps provided in
the configuration, which means that such steps now always are visible by
default in path explanations.
* The `IRGuards` library has improved handling of pointer addition and subtraction operations.

View File

@@ -1,14 +0,0 @@
## 0.9.0
### Breaking Changes
* The `shouldPrintFunction` predicate from `PrintAstConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
* The `shouldPrintFunction` predicate from `PrintIRConfiguration` has been replaced by `shouldPrintDeclaration`. Users should now override `shouldPrintDeclaration` if they want to limit the declarations that should be printed.
### Major Analysis Improvements
* The `PrintAST` library now also prints global and namespace variables and their initializers.
### Minor Analysis Improvements
* The `_Float128x` type is no longer exposed as a builtin type. As this type could not occur any code base, this should only affect queries that explicitly looked at the builtin types.

View File

@@ -1,3 +0,0 @@
## 0.9.1
No user-facing changes.

View File

@@ -1,14 +0,0 @@
## 0.9.2
### Deprecated APIs
* `getAllocatorCall` on `DeleteExpr` and `DeleteArrayExpr` has been deprecated. `getDeallocatorCall` should be used instead.
### New Features
* Added `DeleteOrDeleteArrayExpr` as a super type of `DeleteExpr` and `DeleteArrayExpr`
### Minor Analysis Improvements
* `delete` and `delete[]` are now modeled as calls to the relevant `operator delete` in the IR. In the case of a dynamic delete call a new instruction `VirtualDeleteFunctionAddress` is used to represent a function that dispatches to the correct delete implementation.
* Only the 2 level indirection of `argv` (corresponding to `**argv`) is consided for `FlowSource`.

View File

@@ -1,3 +0,0 @@
## 0.9.3
No user-facing changes.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.11.0
lastReleaseVersion: 0.7.1

View File

@@ -1,3 +0,0 @@
import experimental.cryptography.CryptoArtifact
import experimental.cryptography.CryptoAlgorithmNames
import experimental.cryptography.modules.OpenSSL as OpenSSL

View File

@@ -1,239 +0,0 @@
/**
* Names of known cryptographic algorithms.
* The names are standardized into upper-case, no spaces, dashes or underscores.
*/
/**
* Returns a string to represent generally unknown algorithms.
* Predicate is to be used to get a consistent string representation
* for unknown algorithms.
*/
string unknownAlgorithm() { result = "UNKNOWN" }
string getHashType() { result = "HASH" }
string getSymmetricEncryptionType() { result = "SYMMETRIC_ENCRYPTION" }
string getAsymmetricEncryptionType() { result = "ASYMMETRIC_ENCRYPTION" }
string getKeyDerivationType() { result = "KEY_DERIVATION" }
string getCipherBlockModeType() { result = "BLOCK_MODE" }
string getSymmetricPaddingType() { result = "SYMMETRIC_PADDING" }
string getAsymmetricPaddingType() { result = "ASYMMETRIC_PADDING" }
string getEllipticCurveType() { result = "ELLIPTIC_CURVE" }
string getSignatureType() { result = "SIGNATURE" }
string getKeyExchangeType() { result = "KEY_EXCHANGE" }
string getAsymmetricType() {
result in [
getAsymmetricEncryptionType(), getSignatureType(), getKeyExchangeType(),
getEllipticCurveType()
]
}
predicate isKnownType(string algType) {
algType in [
getHashType(), getSymmetricEncryptionType(), getAsymmetricEncryptionType(),
getKeyDerivationType(), getCipherBlockModeType(), getSymmetricPaddingType(),
getAsymmetricPaddingType(), getEllipticCurveType(), getSignatureType(), getKeyExchangeType()
]
}
predicate isKnownAlgorithm(string name) { isKnownAlgorithm(name, _) }
predicate isKnownAlgorithm(string name, string algType) {
isHashingAlgorithm(name) and algType = "HASH"
or
isEncryptionAlgorithm(name, algType) and
algType in ["SYMMETRIC_ENCRYPTION", "ASYMMETRIC_ENCRYPTION"]
or
isKeyDerivationAlgorithm(name) and algType = "KEY_DERIVATION"
or
isCipherBlockModeAlgorithm(name) and algType = "BLOCK_MODE"
or
isPaddingAlgorithm(name, algType) and algType in ["SYMMETRIC_PADDING", "ASYMMETRIC_PADDING"]
or
isEllipticCurveAlgorithm(name) and algType = "ELLIPTIC_CURVE"
or
isSignatureAlgorithm(name) and algType = "SIGNATURE"
or
isKeyExchangeAlgorithm(name) and algType = "KEY_EXCHANGE"
}
/**
* Holds if `name` is a known hashing algorithm in the model/library.
*/
predicate isHashingAlgorithm(string name) {
name =
[
"BLAKE2", "BLAKE2B", "BLAKE2S", "SHA2", "SHA224", "SHA256", "SHA384", "SHA512", "SHA512224",
"SHA512256", "SHA3", "SHA3224", "SHA3256", "SHA3384", "SHA3512", "SHAKE128", "SHAKE256",
"SM3", "WHIRLPOOL", "POLY1305", "HAVEL128", "MD2", "MD4", "MD5", "PANAMA", "RIPEMD",
"RIPEMD128", "RIPEMD256", "RIPEMD160", "RIPEMD320", "SHA0", "SHA1", "SHA", "MGF1", "MGF1SHA1",
"MDC2", "SIPHASH"
]
}
predicate isEncryptionAlgorithm(string name, string algType) {
isAsymmetricEncryptionAlgorithm(name) and algType = "ASYMMETRIC_ENCRYPTION"
or
isSymmetricEncryptionAlgorithm(name) and algType = "SYMMETRIC_ENCRYPTION"
}
predicate isEncryptionAlgorithm(string name) { isEncryptionAlgorithm(name, _) }
/**
* Holds if `name` corresponds to a known symmetric encryption algorithm.
*/
predicate isSymmetricEncryptionAlgorithm(string name) {
// NOTE: AES is meant to caputure all possible key lengths
name =
[
"AES", "AES128", "AES192", "AES256", "ARIA", "BLOWFISH", "BF", "ECIES", "CAST", "CAST5",
"CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "CHACHA", "CHACHA20",
"CHACHA20POLY1305", "GOST", "GOSTR34102001", "GOSTR341094", "GOSTR341194", "GOST2814789",
"GOSTR341194", "GOST2814789", "GOST28147", "GOSTR341094", "GOST89", "GOST94", "GOST34102012",
"GOST34112012", "IDEA", "RABBIT", "SEED", "SM4", "DES", "DESX", "3DES", "TDES", "2DES",
"DES3", "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2", "ARC4", "RC4", "ARCFOUR", "ARC5",
"RC5", "MAGMA", "KUZNYECHIK"
]
}
/**
* Holds if `name` corresponds to a known key derivation algorithm.
*/
predicate isKeyDerivationAlgorithm(string name) {
name =
[
"ARGON2", "CONCATKDF", "CONCATKDFHASH", "CONCATKDFHMAC", "KBKDFCMAC", "BCRYPT", "HKDF",
"HKDFEXPAND", "KBKDF", "KBKDFHMAC", "PBKDF1", "PBKDF2", "PBKDF2HMAC", "PKCS5", "SCRYPT",
"X963KDF", "EVPKDF"
]
}
/**
* Holds if `name` corresponds to a known cipher block mode
*/
predicate isCipherBlockModeAlgorithm(string name) {
name = ["CBC", "GCM", "CCM", "CFB", "OFB", "CFB8", "CTR", "OPENPGP", "XTS", "EAX", "SIV", "ECB"]
}
/**
* Holds if `name` corresponds to a known padding algorithm
*/
predicate isPaddingAlgorithm(string name, string algType) {
isSymmetricPaddingAlgorithm(name) and algType = "SYMMETRIC_PADDING"
or
isAsymmetricPaddingAlgorithm(name) and algType = "ASYMMETRIC_PADDING"
}
/**
* holds if `name` corresponds to a known symmetric padding algorithm
*/
predicate isSymmetricPaddingAlgorithm(string name) { name = ["PKCS7", "ANSIX923"] }
/**
* Holds if `name` corresponds to a known asymmetric padding algorithm
*/
predicate isAsymmetricPaddingAlgorithm(string name) { name = ["OAEP", "PKCS1V15", "PSS", "KEM"] }
predicate isBrainpoolCurve(string curveName, int keySize) {
// ALL BRAINPOOL CURVES
keySize in [160, 192, 224, 256, 320, 384, 512] and
(
curveName = "BRAINPOOLP" + keySize.toString() + "R1"
or
curveName = "BRAINPOOLP" + keySize.toString() + "T1"
)
}
predicate isSecCurve(string curveName, int keySize) {
// ALL SEC CURVES
keySize in [112, 113, 128, 131, 160, 163, 192, 193, 224, 233, 239, 256, 283, 384, 409, 521, 571] and
exists(string suff | suff in ["R1", "R2", "K1"] |
curveName = "SECT" + keySize.toString() + suff or
curveName = "SECP" + keySize.toString() + suff
)
}
predicate isC2Curve(string curveName, int keySize) {
// ALL C2 CURVES
keySize in [163, 176, 191, 208, 239, 272, 304, 359, 368, 431] and
exists(string pre, string suff |
pre in ["PNB", "ONB", "TNB"] and suff in ["V1", "V2", "V3", "V4", "V5", "W1", "R1"]
|
curveName = "C2" + pre + keySize.toString() + suff
)
}
predicate isPrimeCurve(string curveName, int keySize) {
// ALL PRIME CURVES
keySize in [192, 239, 256] and
exists(string suff | suff in ["V1", "V2", "V3"] | curveName = "PRIME" + keySize.toString() + suff)
}
predicate isEllipticCurveAlgorithm(string curveName) { isEllipticCurveAlgorithm(curveName, _) }
/**
* Holds if `name` corresponds to a known elliptic curve.
*/
predicate isEllipticCurveAlgorithm(string curveName, int keySize) {
isSecCurve(curveName, keySize)
or
isBrainpoolCurve(curveName, keySize)
or
isC2Curve(curveName, keySize)
or
isPrimeCurve(curveName, keySize)
or
curveName = "ES256" and keySize = 256
or
curveName = "CURVE25519" and keySize = 255
or
curveName = "X25519" and keySize = 255
or
curveName = "ED25519" and keySize = 255
or
curveName = "CURVE448" and keySize = 448 // TODO: need to check the key size
or
curveName = "ED448" and keySize = 448
or
curveName = "X448" and keySize = 448
or
curveName = "NUMSP256T1" and keySize = 256
or
curveName = "NUMSP384T1" and keySize = 384
or
curveName = "NUMSP512T1" and keySize = 512
or
curveName = "SM2" and keySize in [256, 512]
}
/**
* Holds if `name` corresponds to a known signature algorithm.
*/
predicate isSignatureAlgorithm(string name) {
name =
[
"DSA", "ECDSA", "EDDSA", "ES256", "ES256K", "ES384", "ES512", "ED25519", "ED448", "ECDSA256",
"ECDSA384", "ECDSA512"
]
}
/**
* Holds if `name` is a key exchange algorithm.
*/
predicate isKeyExchangeAlgorithm(string name) {
name = ["ECDH", "DH", "DIFFIEHELLMAN", "X25519", "X448"]
}
/**
* Holds if `name` corresponds to a known asymmetric encryption.
*/
predicate isAsymmetricEncryptionAlgorithm(string name) { name = ["RSA"] }

View File

@@ -1,316 +0,0 @@
import cpp
private import experimental.cryptography.CryptoAlgorithmNames
import semmle.code.cpp.ir.dataflow.TaintTracking
/*
* A cryptographic artifact is a DataFlow::Node associated with some
* operation, algorithm, or any other aspect of cryptography.
*/
abstract class CryptographicArtifact extends Expr { }
// /**
// * Associates a symmetric encryption algorithm with a block mode.
// * The DataFlow::Node representing this association should be the
// * point where the algorithm and block mode are combined.
// * This may be at the call to encryption or in the construction
// * of an object prior to encryption.
// */
// abstract class SymmetricCipher extends CryptographicArtifact{
// abstract SymmetricEncryptionAlgorithm getEncryptionAlgorithm();
// abstract BlockMode getBlockMode();
// final predicate hasBlockMode(){
// exists(this.getBlockMode())
// }
// }
// /**
// * A cryptographic operation is a method call that invokes a cryptographic
// * algorithm (encrypt/decrypt) or a function in support of a cryptographic algorithm
// * (key generation).
// *
// * Since operations are related to or in support of algorithms, operations must
// * provide a reference to their associated algorithm. Often operataions themselves
// * encapsulate algorithms, so operations can also extend CryptographicAlgorithm
// * and refer to themselves as the target algorithm.
// */
// abstract class CryptographicOperation extends CryptographicArtifact, Call{
// // bindingset[paramName, ind]
// // final DataFlow::Node getParameterSource(int ind, string paramName){
// // result = Utils::getUltimateSrcFromApiNode(this.(API::CallNode).getParameter(ind, paramName))
// // }
// final string getAlgorithmName(){
// if exists(this.getAlgorithm().getName())
// then result = this.getAlgorithm().getName()
// else result = unknownAlgorithm()
// }
// final predicate hasAlgorithm(){
// exists(this.getAlgorithm())
// }
// final predicate isUnknownAlgorithm(){
// this.getAlgorithmName() = unknownAlgorithm()
// or
// not this.hasAlgorithm()
// }
// // TODO: this might have to be parameterized by a configuration source for
// // situations where an operation is passed an algorithm
// abstract CryptographicAlgorithm getAlgorithm();
// }
// /** A key generation operation for asymmetric keys */
// abstract class KeyGen extends CryptographicOperation{
// int getAKeySizeInBits(){
// result = getKeySizeInBits(_)
// }
// final predicate hasKeySize(Expr configSrc){
// exists(this.getKeySizeInBits(configSrc))
// }
// final predicate hasKeySize(){
// exists(this.getAKeySizeInBits())
// }
// abstract Expr getKeyConfigSrc();
// abstract int getKeySizeInBits(Expr configSrc);
// }
abstract class CryptographicOperation extends CryptographicArtifact, Call { }
abstract class KeyGeneration extends CryptographicOperation {
// TODO: what if the algorithm is UNKNOWN?
abstract Expr getKeyConfigurationSource(CryptographicAlgorithm alg);
abstract CryptographicAlgorithm getAlgorithm();
int getKeySizeInBits(CryptographicAlgorithm alg) {
result = this.getKeyConfigurationSource(alg).(Literal).getValue().toInt()
}
predicate hasConstantKeySize(CryptographicAlgorithm alg) { exists(this.getKeySizeInBits(alg)) }
predicate hasKeyConfigurationSource(CryptographicAlgorithm alg) {
exists(this.getKeyConfigurationSource(alg))
}
Expr getAKeyConfigurationSource() { result = this.getKeyConfigurationSource(_) }
}
abstract class AsymmetricKeyGeneration extends KeyGeneration { }
abstract class SymmetricKeyGeneration extends KeyGeneration { }
/**
* A cryptographic algorithm is a `CryptographicArtifact`
* representing a cryptographic algorithm (see `CryptoAlgorithmNames.qll`).
* Cryptographic algorithms can be functions referencing common crypto algorithms (e.g., hashlib.md5)
* or strings that are used in cryptographic operation configurations (e.g., hashlib.new("md5")).
* Cryptogrpahic algorithms may also be operations that wrap or abstract one or
* more algorithms (e.g., cyrptography.fernet.Fernet and AES, CBC and PKCS7).
*
* In principle, this class should model the location where an algorithm enters the program, not
* necessarily where it is used.
*/
abstract class CryptographicAlgorithm extends CryptographicArtifact {
abstract string getName();
abstract string getAlgType();
// string getAlgType(){
// if this instanceof HashAlgorithm then result = getHashType()
// else if this instanceof KeyDerivationAlgorithm then result = getKeyDerivationType()
// else if this instanceof SymmetricEncryptionAlgorithm then result = getSymmetricEncryptionType()
// else if this instanceof AsymmetricEncryptionAlgorithm then result = getAsymmetricEncryptionType()
// else if this instanceof SymmetricEncryptionAlgorithm then result = getSymmetricPaddingType()
// else if this instanceof AsymmetricEncryptionAlgorithm then result = getAsymmetricPaddingType()
// else if this instanceof EllipticCurveAlgorithm then result = getEllipticCurveType()
// else if this instanceof BlockMode then result = getCipherBlockModeType()
// else if this instanceof KeyExchangeAlgorithm then result = getKeyExchangeType()
// else if this instanceof SigningAlgorithm then result = getSignatureType()
// else result = unknownAlgorithm()
// }
// TODO: handle case where name isn't known, not just unknown?
/**
* Normalizes a raw name into a normalized name as found in `CryptoAlgorithmNames.qll`.
* Subclassess should override for more api-specific normalization.
* By deafult, converts a raw name to upper-case with no hyphen, underscore, hash, or space.
*/
bindingset[s]
string normalizeName(string s) {
exists(string normStr | normStr = s.toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
result = normStr and isKnownAlgorithm(result)
or
result = unknownAlgorithm() and not isKnownAlgorithm(normStr)
)
}
abstract Expr configurationSink();
predicate hasConfigurationSink() { exists(this.configurationSink()) }
}
abstract class HashAlgorithm extends CryptographicAlgorithm {
final string getHashName() {
if exists(string n | n = this.getName() and isHashingAlgorithm(n))
then isHashingAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
override string getAlgType() { result = getHashType() }
}
abstract class KeyDerivationAlgorithm extends CryptographicAlgorithm {
final string getKDFName() {
if exists(string n | n = this.getName() and isKeyDerivationAlgorithm(n))
then isKeyDerivationAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
override string getAlgType() { result = getKeyDerivationType() }
}
// abstract class KeyDerivationOperation extends CryptographicOperation{
// DataFlow::Node getIterationSizeSrc(){
// none()
// }
// DataFlow::Node getSaltConfigSrc(){
// none()
// }
// DataFlow::Node getHashConfigSrc(){
// none()
// }
// // TODO: get encryption algorithm for CBC-based KDF?
// DataFlow::Node getDerivedKeySizeSrc(){
// none()
// }
// DataFlow::Node getModeSrc(){
// none()
// }
// // TODO: add more to cover all the parameters of most KDF operations? Perhaps subclass for each type?
// abstract predicate requiresIteration();
// abstract predicate requiresSalt();
// abstract predicate requiresHash();
// //abstract predicate requiresKeySize(); // Going to assume all requires a size
// abstract predicate requiresMode();
// }
abstract class EncryptionAlgorithm extends CryptographicAlgorithm {
final predicate isAsymmetric() { this instanceof AsymmetricEncryptionAlgorithm }
final predicate isSymmetric() { not this.isAsymmetric() }
// NOTE: DO_NOT add getEncryptionName here, we rely on the fact the parent
// class does not have this common predicate.
}
/**
* A parent class to represent any algorithm for which
* asymmetric cryptography is involved.
* Intended to be distinct from AsymmetricEncryptionAlgorithm
* which is intended only for asymmetric algorithms that specifically encrypt.
*/
abstract class AsymmetricAlgorithm extends CryptographicAlgorithm { }
/**
* Algorithms directly or indirectly related to asymmetric encryption,
* e.g., RSA, DSA, but also RSA padding algorithms
*/
abstract class AsymmetricEncryptionAlgorithm extends AsymmetricAlgorithm, EncryptionAlgorithm {
final string getEncryptionName() {
if exists(string n | n = this.getName() and isAsymmetricEncryptionAlgorithm(n))
then isAsymmetricEncryptionAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
override string getAlgType() { result = getAsymmetricEncryptionType() }
}
/**
* Algorithms directly or indirectly related to symmetric encryption,
* e.g., AES, DES, but also block modes and padding
*/
abstract class SymmetricEncryptionAlgorithm extends EncryptionAlgorithm {
final string getEncryptionName() {
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
then isSymmetricEncryptionAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
// TODO: add a stream cipher predicate?
override string getAlgType() { result = getSymmetricEncryptionType() }
}
// Used only to categorize all padding into a single object,
// DO_NOT add predicates here. Only for categorization purposes.
abstract class PaddingAlgorithm extends CryptographicAlgorithm { }
abstract class SymmetricPadding extends PaddingAlgorithm {
final string getPaddingName() {
if exists(string n | n = this.getName() and isSymmetricPaddingAlgorithm(n))
then isSymmetricPaddingAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
override string getAlgType() { result = getSymmetricPaddingType() }
}
abstract class AsymmetricPadding extends PaddingAlgorithm {
final string getPaddingName() {
if exists(string n | n = this.getName() and isAsymmetricPaddingAlgorithm(n))
then isAsymmetricPaddingAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
override string getAlgType() { result = getAsymmetricPaddingType() }
}
abstract class EllipticCurveAlgorithm extends AsymmetricAlgorithm {
final string getCurveName() {
if exists(string n | n = this.getName() and isEllipticCurveAlgorithm(n))
then isEllipticCurveAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
final int getCurveBitSize() { isEllipticCurveAlgorithm(this.getCurveName(), result) }
override string getAlgType() { result = getEllipticCurveType() }
}
abstract class BlockModeAlgorithm extends CryptographicAlgorithm {
final string getBlockModeName() {
if exists(string n | n = this.getName() and isCipherBlockModeAlgorithm(n))
then isCipherBlockModeAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
/**
* Gets the source of the IV configuration.
*/
abstract Expr getIVorNonce();
final predicate hasIVorNonce() { exists(this.getIVorNonce()) }
override string getAlgType() { result = getCipherBlockModeType() }
}
// abstract class KeyWrapOperation extends CryptographicOperation{
// }
abstract class AuthenticatedEncryptionAlgorithm extends SymmetricEncryptionAlgorithm {
final string getAuthticatedEncryptionName() {
if exists(string n | n = this.getName() and isSymmetricEncryptionAlgorithm(n))
then isSymmetricEncryptionAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
}
abstract class KeyExchangeAlgorithm extends AsymmetricAlgorithm {
final string getKeyExchangeName() {
if exists(string n | n = this.getName() and isKeyExchangeAlgorithm(n))
then isKeyExchangeAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
override string getAlgType() { result = getKeyExchangeType() }
}
abstract class SigningAlgorithm extends AsymmetricAlgorithm {
final string getSigningName() {
if exists(string n | n = this.getName() and isSignatureAlgorithm(n))
then isSignatureAlgorithm(result) and result = this.getName()
else result = unknownAlgorithm()
}
override string getAlgType() { result = getSignatureType() }
}

View File

@@ -1,718 +0,0 @@
import cpp
import experimental.cryptography.CryptoAlgorithmNames
import experimental.cryptography.CryptoArtifact
import experimental.cryptography.utils.OpenSSL.CryptoFunction
import experimental.cryptography.utils.OpenSSL.AlgorithmSink
import experimental.cryptography.utils.OpenSSL.PassthroughFunction
import experimental.cryptography.utils.OpenSSL.CryptoAlgorithm
import experimental.cryptography.CryptoArtifact
// import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.ir.dataflow.DataFlow
/**
* Problematic case in OpenSSL speed.c
* static const char *names[ALGOR_NUM] = {
* "md2", "mdc2", "md4", "md5", "sha1", "rmd160",
* "sha256", "sha512", "whirlpool", "hmac(md5)",
* "des-cbc", "des-ede3", "rc4", "idea-cbc", "seed-cbc",
* "rc2-cbc", "rc5-cbc", "blowfish", "cast-cbc",
* "aes-128-cbc", "aes-192-cbc", "aes-256-cbc",
* "camellia-128-cbc", "camellia-192-cbc", "camellia-256-cbc",
* "evp", "ghash", "rand", "cmac"
* };
*
* Every entry is considered a block mode, hash, and symmetric encryption algorithm
* getEncryptionName for example, will return unknown
*/
predicate nodeToExpr(DataFlow::Node node, Expr e) {
e = node.asExpr() or e = node.asIndirectArgument()
}
Expr getExprFromNode(DataFlow::Node node) { nodeToExpr(node, result) }
DataFlow::Node getNodeFromExpr(Expr e) { nodeToExpr(result, e) }
predicate isEVP_PKEY_CTX(Type t) { t.getUnderlyingType().stripType().getName() = "evp_pkey_ctx_st" }
/**
* An expression representing an EVP_PKEY_CTX* at the location of a
* known AlgorithmSinkArgument.
* The EVP_PKEY_CTX* represents the location where the CTX is tied to the algorithm,
* and can be used as a source for tracing EVP_PKEY_CTX to other operations.
*/
class Known_EVP_PKEY_CTX_Ptr_Source extends Expr {
Known_EVP_PKEY_CTX_Ptr_Source() {
isEVP_PKEY_CTX(this.getUnderlyingType()) and
this.getUnderlyingType() instanceof PointerType and
exists(AlgorithmSinkArgument arg, Call sinkCall |
arg.getSinkCall() = sinkCall and
sinkCall.getAnArgument() = this
or
this = sinkCall
)
}
}
// module CTXFlow implements DataFlow::ConfigSig{
// predicate isSource(DataFlow::Node source) {
// // ASSUMPTION: at a sink, an algorithm is converted into a CTX through a return of the call only
// // and is the primary source of interest for CTX tracing
// source.asExpr() instanceof AlgorithmSinkArgument
// }
// predicate isSink(DataFlow::Node sink){
// sink.asExpr() instanceof CTXSink
// }
// predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
// // cls.getName() = "asn1_object_st" flow out on any EVP_PKEY_CTX which is "evp_pkey_ctx_st"
// exists(Call c |
// isEVP_PKEY_CTX(c.getUnderlyingType()) and
// node1.asExpr() = c.getAnArgument() and c = node2.asExpr())
// }
// }
// module CTXFlowConfig = DataFlow::Global<CTXFlow>;
// TODO: currently only handles tracing from literals to sinks
module LiteralAlgorithmTracerConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof Literal and
// Optimization to reduce literal tracing on integers to only those that are known/relevant NIDs.
(
exists(source.asExpr().getValue().toInt())
implies
source.asExpr().getValue().toInt() < getNIDMax()
) and
// False positives observed inside OBJ_nid2* and OBJ_sn2* functions where NULL is a possible assignment.
// While this is a concern, it only occurs if the object being referenced is NULL to begin with
// Perhaps a different query should be used to find these caes if they represent a threat.
// Filter out any open ssl function source in a function namae Obj_*
// False positives in OpenSSL also observed for CRYPTO_strndup (filtering any CRYPTO_* function)
// due to setting a null byte in the string
(
isPossibleOpenSSLFunction(source.getEnclosingCallable())
implies
(
not source.getEnclosingCallable().getName().matches("OBJ_%") and
not source.getEnclosingCallable().getName().matches("CRYPTO_%")
)
)
}
predicate isSink(DataFlow::Node sink) {
// A sink is a call to a function that takes an algorithm as an argument
// must include checks for asIndirectArgument since the input may be a pointer to an object
// and the member of the object holds the algorithm on the trace.
getExprFromNode(sink) instanceof AlgorithmSinkArgument
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
knownPassThroughStep(node1, node2)
}
predicate isBarrier(DataFlow::Node node) {
// If the node is the 'next' argument of a isCallPassThrough, it is only allowed if it is an out parameter
// i.e., a defining argument. This barrier says that if the node is an expression not an out parameter, it is filtered.
// Out arguments will not be filtered.
exists(Call c | knownPassthoughCall(c, _, node.asExpr()) and c.getAnArgument() = node.asExpr())
or
// False positive reducer, don't flow out through argv
node.asVariable().hasName("argv")
or
node.asIndirectVariable().hasName("argv")
}
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
// Assume a read on crypto identifying field for any object of type asn1_object_st (i.e., ASN1_OBJECT)
exists(Class cls | cls.getName() = "asn1_object_st" |
node.getType().getUnspecifiedType().stripType() = cls and
c.(DataFlow::FieldContent).getField() = cls.getAMember() and
c.(DataFlow::FieldContent).getField().getName() in ["nid", "sn", "ln"]
)
}
}
module LiteralAlgorithmTracer = DataFlow::Global<LiteralAlgorithmTracerConfig>;
/**
* `source` is an expression that is a source of an algorithm of type `algType`.
* `algType` may be `UNKONWN`.
* See CryptoAlgorithmNames for other possible values of `algType`.
*/
bindingset[sinkAlgType]
predicate hasLiteralPathToAlgSink(DataFlow::Node source, DataFlow::Node sink, string sinkAlgType) {
LiteralAlgorithmTracer::flow(source, sink) and
getExprFromNode(sink).(AlgorithmSinkArgument).algType() = sinkAlgType
}
private predicate knownTracedAlgorithm(Literal e, string srcSinkType) {
knownTracedAlgorithm(e, srcSinkType, srcSinkType)
}
private predicate knownTracedAlgorithm(Literal e, string srcType, string sinkType) {
resolveAlgorithmFromLiteral(e, _, srcType) and
hasLiteralPathToAlgSink(DataFlow::exprNode(e), _, sinkType) and
isKnownType(sinkType) and
isKnownType(srcType)
}
private predicate unknownTracedLiteralAlgorithm(Literal e, string srcSinkType) {
// Asymmetric special case:
// Since asymmetric algorithm sinks are used for various categories of asymmetric algorithms
// an asymmetric algorithm is only unknown if there is no trace from any asymmetric type to the given srcSinkType sink
if getAsymmetricType() = srcSinkType
then forall(string t | t = getAsymmetricType() | unknownTracedLiteralAlgorithm(e, t, srcSinkType))
else unknownTracedLiteralAlgorithm(e, srcSinkType, srcSinkType)
}
private predicate unknownTracedLiteralAlgorithm(Literal e, string srcType, string sinkType) {
// the literal resolves to an algorithm, but not to the sinktype
// or generally doesn't resolve to any algorithm type
// this case covers 'nonsense' cases e.g., use RSA for symmetric encryption
not resolveAlgorithmFromLiteral(e, _, srcType) and
isValidAlgorithmLiteral(e) and
hasLiteralPathToAlgSink(DataFlow::exprNode(e), _, sinkType) and
isKnownType(sinkType) and
isKnownType(srcType)
}
private predicate unknownTracedNonLiteralAlgorithm(AlgorithmSinkArgument e, string srcSinkType) {
// Asymmetric special case:
// Since asymmetric algorithm sinks are used for various categories of asymmetric algorithms
// an asymmetric algorithm is only unknown if there is no trace from any asymmetric type to the given srcSinkType sink
if getAsymmetricType() = srcSinkType
then
forall(string t | t = getAsymmetricType() | unknownTracedNonLiteralAlgorithm(e, t, srcSinkType))
else unknownTracedNonLiteralAlgorithm(e, srcSinkType, srcSinkType)
}
private predicate unknownTracedNonLiteralAlgorithm(
AlgorithmSinkArgument e, string srcType, string sinkType
) {
not hasLiteralPathToAlgSink(_, getNodeFromExpr(e), srcType) and
LiteralAlgorithmTracerConfig::isSink(getNodeFromExpr(e)) and
e.algType() = sinkType and
isKnownType(srcType) and
isKnownType(sinkType)
}
private predicate functionAlgorithm(Call c, string algType) {
isOpenSSLCryptoFunctionCall(c, _, algType)
}
abstract class OpenSSLTracedAlgorithm extends CryptographicAlgorithm {
override string getName() { resolveAlgorithmFromLiteral(this, result, this.getAlgType()) }
override Expr configurationSink() {
exists(DataFlow::Node sink |
hasLiteralPathToAlgSink(DataFlow::exprNode(this), sink, this.getAlgType())
|
result = getExprFromNode(sink)
)
}
}
abstract class OpenSSLFunctionAlgorithm extends CryptographicAlgorithm {
override string getName() { isOpenSSLCryptoFunctionCall(this, result, this.getAlgType()) }
override Expr configurationSink() { result = this }
}
abstract class OpenSSLUnknownTracedLiteralAlgorithm extends CryptographicAlgorithm {
override string getName() { result = unknownAlgorithm() }
override Expr configurationSink() {
exists(DataFlow::Node sink |
hasLiteralPathToAlgSink(DataFlow::exprNode(this), sink, this.getAlgType())
|
result = getExprFromNode(sink)
)
}
}
abstract class OpenSSLUnknownTracedNonLiteralAlgorithm extends CryptographicAlgorithm {
override string getName() { result = unknownAlgorithm() }
override Expr configurationSink() { result = this }
}
module SymmetricEncryption {
abstract class OpenSSLSymmetricEncryptionAlgorithm extends SymmetricEncryptionAlgorithm { }
class OpenSSLSymmetricEncryptionTracedAlgorithm extends OpenSSLTracedAlgorithm,
OpenSSLSymmetricEncryptionAlgorithm
{
OpenSSLSymmetricEncryptionTracedAlgorithm() {
knownTracedAlgorithm(this, getSymmetricEncryptionType())
}
}
class OpenSSLSymmetricEncryptionFunctionAlgorithm extends OpenSSLFunctionAlgorithm,
OpenSSLSymmetricEncryptionAlgorithm
{
OpenSSLSymmetricEncryptionFunctionAlgorithm() {
functionAlgorithm(this, getSymmetricEncryptionType())
}
}
class OpenSSLSymmetricEncryptionTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
OpenSSLSymmetricEncryptionAlgorithm
{
OpenSSLSymmetricEncryptionTracedUnknownLiteralAlgorithm() {
unknownTracedLiteralAlgorithm(this, getSymmetricEncryptionType())
}
}
class OpenSSLSymmetricEncryptionUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
OpenSSLSymmetricEncryptionAlgorithm
{
OpenSSLSymmetricEncryptionUnknownNonLiteralTracedAlgorithm() {
unknownTracedNonLiteralAlgorithm(this, getSymmetricEncryptionType())
}
}
}
module BlockModes {
/**
* In OpenSSL, block modes are associated directly with symmetric encryption algorithms.
* As such, OpenSSLBLockModes are modeled as extensions of any openssl symmetric encryption algorithm
*/
class OpenSSLBlockModeAlgorithm extends BlockModeAlgorithm, Expr instanceof SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm
{
OpenSSLBlockModeAlgorithm() {
//two cases, either the block mode is a literal or it is a function call
resolveAlgorithmFromLiteral(this, _, "BLOCK_MODE")
or
isOpenSSLCryptoFunctionCall(this, _, "BLOCK_MODE")
}
override string getName() {
resolveAlgorithmFromLiteral(this, result, "BLOCK_MODE")
or
isOpenSSLCryptoFunctionCall(this, result, "BLOCK_MODE")
}
override Expr configurationSink() {
result = this.(SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm).configurationSink()
}
override Expr getIVorNonce() {
// TODO
none()
}
}
class UnknownOpenSSLBlockModeAlgorithm extends BlockModeAlgorithm, Expr instanceof SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm
{
UnknownOpenSSLBlockModeAlgorithm() {
//two cases, either the block mode is a literal or it is a function call
not resolveAlgorithmFromLiteral(this, _, "BLOCK_MODE") and
not isOpenSSLCryptoFunctionCall(this, _, "BLOCK_MODE")
}
override string getName() { result = unknownAlgorithm() }
override Expr configurationSink() {
result = this.(SymmetricEncryption::OpenSSLSymmetricEncryptionAlgorithm).configurationSink()
}
override Expr getIVorNonce() { none() }
}
}
module Hashes {
abstract class OpenSSLHashAlgorithm extends HashAlgorithm { }
class OpenSSLHashTracedAlgorithm extends OpenSSLTracedAlgorithm, OpenSSLHashAlgorithm {
OpenSSLHashTracedAlgorithm() { knownTracedAlgorithm(this, getHashType()) }
}
class OpenSSLHashFunctionAlgorithm extends OpenSSLFunctionAlgorithm, OpenSSLHashAlgorithm {
OpenSSLHashFunctionAlgorithm() { functionAlgorithm(this, getHashType()) }
}
class OpenSSLHashTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
OpenSSLHashAlgorithm
{
OpenSSLHashTracedUnknownLiteralAlgorithm() {
unknownTracedLiteralAlgorithm(this, getHashType())
}
}
class OpenSSLHashUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
OpenSSLHashAlgorithm
{
OpenSSLHashUnknownNonLiteralTracedAlgorithm() {
unknownTracedNonLiteralAlgorithm(this, getHashType())
}
}
class OpenSSLNullHash extends HashAlgorithm {
OpenSSLNullHash() {
exists(Call c |
this = c and
isPossibleOpenSSLFunction(c.getTarget()) and
c.getTarget().getName() in ["EVP_md_null"]
)
}
override string getName() { result = unknownAlgorithm() }
override Expr configurationSink() { result = this }
}
}
module EllipticCurves {
// TODO: need to address EVP_PKEY_Q_keygen where the type is "EC" but the curve is UNKNOWN?
class OpenSSLEllipticCurveTracedAlgorithm extends OpenSSLTracedAlgorithm, EllipticCurveAlgorithm {
OpenSSLEllipticCurveTracedAlgorithm() { knownTracedAlgorithm(this, getEllipticCurveType()) }
}
class OpenSSLEllipticCurveFunctionAlgorithm extends OpenSSLFunctionAlgorithm,
EllipticCurveAlgorithm
{
OpenSSLEllipticCurveFunctionAlgorithm() { functionAlgorithm(this, getEllipticCurveType()) }
}
class OpenSSLEllipticCurveTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
EllipticCurveAlgorithm
{
OpenSSLEllipticCurveTracedUnknownLiteralAlgorithm() {
unknownTracedLiteralAlgorithm(this, getEllipticCurveType())
}
}
class OpenSSLEllipticCurvehUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
EllipticCurveAlgorithm
{
OpenSSLEllipticCurvehUnknownNonLiteralTracedAlgorithm() {
unknownTracedNonLiteralAlgorithm(this, getEllipticCurveType())
}
}
// https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_ex.html
class OpenSSLNullEllipticCurve extends EllipticCurveAlgorithm {
OpenSSLNullEllipticCurve() {
exists(Call c |
this = c and
isPossibleOpenSSLFunction(c.getTarget()) and
c.getTarget().getName() in ["EC_KEY_new", "EC_KEY_new_ex"]
)
}
override string getName() { result = unknownAlgorithm() }
override Expr configurationSink() { result = this }
}
}
module AsymmetricEncryption {
class OpenSSLAsymmetricEncryptionTracedAlgorithm extends OpenSSLTracedAlgorithm,
AsymmetricEncryptionAlgorithm
{
OpenSSLAsymmetricEncryptionTracedAlgorithm() {
knownTracedAlgorithm(this, getAsymmetricEncryptionType())
}
}
class OpenSSLAsymmetricEncryptionFunctionAlgorithm extends OpenSSLFunctionAlgorithm,
AsymmetricEncryptionAlgorithm
{
OpenSSLAsymmetricEncryptionFunctionAlgorithm() {
functionAlgorithm(this, getAsymmetricEncryptionType())
}
}
class OpenSSLAsymmetricEncryptionTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
AsymmetricEncryptionAlgorithm
{
OpenSSLAsymmetricEncryptionTracedUnknownLiteralAlgorithm() {
unknownTracedLiteralAlgorithm(this, getAsymmetricEncryptionType())
}
}
class OpenSSLAsymmetricEncryptionUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
AsymmetricEncryptionAlgorithm
{
OpenSSLAsymmetricEncryptionUnknownNonLiteralTracedAlgorithm() {
unknownTracedNonLiteralAlgorithm(this, getAsymmetricEncryptionType())
}
}
}
module SigningAlgorithms {
class OpenSSLSignatureTracedAlgorithm extends OpenSSLTracedAlgorithm, SigningAlgorithm {
OpenSSLSignatureTracedAlgorithm() { knownTracedAlgorithm(this, getSignatureType()) }
}
class OpenSSLSignatureFunctionAlgorithm extends OpenSSLFunctionAlgorithm, SigningAlgorithm {
OpenSSLSignatureFunctionAlgorithm() { functionAlgorithm(this, getSignatureType()) }
}
class OpenSSLSignatureTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
SigningAlgorithm
{
OpenSSLSignatureTracedUnknownLiteralAlgorithm() {
unknownTracedLiteralAlgorithm(this, getSignatureType())
}
}
class OpenSSLSignatureUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
SigningAlgorithm
{
OpenSSLSignatureUnknownNonLiteralTracedAlgorithm() {
unknownTracedNonLiteralAlgorithm(this, getSignatureType())
}
}
}
module KeyExchange {
class OpenSSLKeyExchangeTracedAlgorithm extends OpenSSLTracedAlgorithm, KeyExchangeAlgorithm {
OpenSSLKeyExchangeTracedAlgorithm() { knownTracedAlgorithm(this, getKeyExchangeType()) }
}
class OpenSSLKeyExchangeFunctionAlgorithm extends OpenSSLFunctionAlgorithm, KeyExchangeAlgorithm {
OpenSSLKeyExchangeFunctionAlgorithm() { functionAlgorithm(this, getKeyExchangeType()) }
}
class OpenSSLKeyExchangeTracedUnknownLiteralAlgorithm extends OpenSSLUnknownTracedLiteralAlgorithm,
KeyExchangeAlgorithm
{
OpenSSLKeyExchangeTracedUnknownLiteralAlgorithm() {
unknownTracedLiteralAlgorithm(this, getKeyExchangeType())
}
}
class OpenSSLKeyExchangeUnknownNonLiteralTracedAlgorithm extends OpenSSLUnknownTracedNonLiteralAlgorithm,
KeyExchangeAlgorithm
{
OpenSSLKeyExchangeUnknownNonLiteralTracedAlgorithm() {
unknownTracedNonLiteralAlgorithm(this, getKeyExchangeType())
}
}
}
module KeyGeneration {
/**
* Functions that explicitly set key generation parameters.
* `sizeInd` is the parameter specifying the size of the key.
* `outInd` is the parameter or return value that the key is written to.
* `outInd` is -1 if the key is written to the return value.
*/
predicate isAsymmetricKeyGenExplicitAlgorithm(Function func, int sizeInd, int outInd) {
isPossibleOpenSSLFunction(func) and
exists(string name | func.hasGlobalName(name) |
name in [
"EVP_PKEY_CTX_set_dsa_paramgen_bits", "DSA_generate_parameters_ex",
"EVP_PKEY_CTX_set_rsa_keygen_bits", "RSA_generate_key_ex", "RSA_generate_key_fips",
"EVP_PKEY_CTX_set_dh_paramgen_prime_len", "DH_generate_parameters_ex"
] and
sizeInd = 1 and
outInd = 0
or
name in ["DSA_generate_parameters", "RSA_generate_key", "DH_generate_parameters"] and
sizeInd = 0 and
outInd = -1
) and
exists(Type t |
(
if sizeInd = -1
then t = func.getType().getUnderlyingType()
else t = func.getParameter(sizeInd).getUnderlyingType()
) and
t instanceof IntegralType and
not t instanceof CharType
)
}
module AsymExplicitAlgKeyLengthFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
// Optimizations to avoid tracing all integers
node.asExpr().(Literal).getValue().toInt() > 0 and // exclude sentinel values
node.asExpr().(Literal).getValue().toInt() < 8500
}
predicate isSink(DataFlow::Node node) {
exists(FunctionCall c, int sizeInd |
isAsymmetricKeyGenExplicitAlgorithm(c.getTarget(), sizeInd, _) and
c.getArgument(sizeInd) = node.asExpr()
)
}
}
module AsymExplicitAlgKeyLengthFlow = DataFlow::Global<AsymExplicitAlgKeyLengthFlowConfig>;
class OpenSSLAsymmetricKeyGenTiedToAlgorithm extends AsymmetricKeyGeneration {
OpenSSLAsymmetricKeyGenTiedToAlgorithm() {
exists(Call c |
this = c and
isPossibleOpenSSLFunction(c.getTarget()) and
isAsymmetricKeyGenExplicitAlgorithm(c.getTarget(), _, _)
)
}
override CryptographicAlgorithm getAlgorithm() { result = this }
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg) {
alg = this and
exists(int sizeInd |
isAsymmetricKeyGenExplicitAlgorithm(this.getTarget(), sizeInd, _) and
AsymExplicitAlgKeyLengthFlow::flow(DataFlow::exprNode(result),
DataFlow::exprNode(this.getArgument(sizeInd)))
)
}
}
module Length_to_RSA_EVP_PKEY_Q_keygen_Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
// Optimizations to avoid tracing all integers
node.asExpr().(Literal).getValue().toInt() > 0 and // exclude sentinel values
node.asExpr().(Literal).getValue().toInt() < 5000
}
predicate isSink(DataFlow::Node node) {
exists(FunctionCall c |
c.getTarget().getName() = "EVP_PKEY_Q_keygen" and
isPossibleOpenSSLFunction(c.getTarget()) and
c.getArgument(3) = node.asExpr()
)
}
}
module Length_to_RSA_EVP_PKEY_Q_keygen_Flow =
DataFlow::Global<Length_to_RSA_EVP_PKEY_Q_keygen_Config>;
class OpenSSL_RSA_EVP_PKEY_Q_keygen extends AsymmetricKeyGeneration {
OpenSSL_RSA_EVP_PKEY_Q_keygen() {
exists(Call c |
this = c and
isPossibleOpenSSLFunction(c.getTarget()) and
this.getTarget().getName() = "EVP_PKEY_Q_keygen" and
this.getArgument(3).getUnderlyingType() instanceof IntegralType
)
}
override CryptographicAlgorithm getAlgorithm() {
result.configurationSink().(AlgorithmSinkArgument).getSinkCall() = this
}
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg) {
alg = this.getAlgorithm() and
Length_to_RSA_EVP_PKEY_Q_keygen_Flow::flow(DataFlow::exprNode(result),
DataFlow::exprNode(this.getArgument(3)))
}
}
predicate isKeyGenOperationWithNoSize(Function func) {
isPossibleOpenSSLFunction(func) and
exists(string name | func.hasGlobalName(name) |
name in ["EVP_PKEY_keygen", "DSA_generate_key", "DH_generate_key", "EVP_PKEY_generate"]
)
}
module KeyGenKeySizeInitToKeyGenConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) {
exists(Call c, Function func, int outInd |
isAsymmetricKeyGenExplicitAlgorithm(func, _, outInd) and
c.getTarget() = func
|
if outInd = -1 then node.asExpr() = c else node.asExpr() = c.getArgument(outInd)
)
}
predicate isSink(DataFlow::Node node) {
exists(Call c |
isKeyGenOperationWithNoSize(c.getTarget()) and c.getAnArgument() = node.asExpr()
)
}
}
module KeyGenKeySizeInitToKeyGenFlow = DataFlow::Global<KeyGenKeySizeInitToKeyGenConfig>;
predicate isEVP_PKEY_CTX_Source(DataFlow::Node node, CryptographicAlgorithm alg) {
exists(Call c |
alg.configurationSink().(AlgorithmSinkArgument).getSinkCall() = c and
(
node.asExpr() = c
or
node.asExpr() = c.getAnArgument()
or
node.asDefiningArgument() = c.getAnArgument()
)
) and
(
node.asExpr() instanceof Known_EVP_PKEY_CTX_Ptr_Source
or
node.asDefiningArgument() instanceof Known_EVP_PKEY_CTX_Ptr_Source
)
}
predicate isKeyGen_EVP_PKEY_CTX_Sink(DataFlow::Node node, Call c) {
isKeyGenOperationWithNoSize(c.getTarget()) and nodeToExpr(node, c.getAnArgument())
}
/**
* Trace from EVP_PKEY_CTX* at algorithm sink to keygen,
* users can then extrapolatae the matching algorithm from the alg sink to the keygen
*/
module EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { isEVP_PKEY_CTX_Source(source, _) }
predicate isSink(DataFlow::Node sink) { isKeyGen_EVP_PKEY_CTX_Sink(sink, _) }
}
module EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize_Flow =
DataFlow::Global<EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize>;
/**
* UNKNOWN key sizes to general purpose key generation functions (i.e., that take in no key size and assume
* is it set on context prior to the call). No path from a key configuration to these operations
* means the key size is UNKNOWN, or more precisely the key size is DEFAULT but
* the defaults can change with each version of OpenSSL, we simply assume the size is generally UNKNOWN.
* ASSUMPTION/TODO: we currently model all known locations where a key size is set explicitly.
* When a key is set implicitly, this usually means a key generation operation
* is called where the operation takes in no key size, and no flow to this operation
* initializes the context with a key size.
* Currently, without a definitive source (set of sources) to start tracing from, we cannot determine
* determine if a single path exists that initializes the context with a key size and another that doesn't.
* Rather than attempt to model all possible sources, we assume that if no path
* from a key config location reaches a generic key generation operation, then the key size is not set.
* NOTE: while this is true, it is possible a key size is set in one path, but not in another
* meaning this approach (and other similar approaches used in this model for UNKNOWN)
* can produce false negatives.
*/
class OpenSSLDefaultKeyGeneration extends AsymmetricKeyGeneration {
OpenSSLDefaultKeyGeneration() {
// this is a call to a function matching isKeyGenOperationWithNoSize
// and there is no flow from a key configuration source to this call
exists(Call c |
this = c and
isKeyGenOperationWithNoSize(this.getTarget()) and
not exists(DataFlow::Node src, DataFlow::Node sink |
KeyGenKeySizeInitToKeyGenFlow::flow(src, sink) and
nodeToExpr(sink, this.getAnArgument())
)
)
}
override CryptographicAlgorithm getAlgorithm() {
if this.getTarget().getName() in ["DSA_generate_key", "DH_generate_key"]
then result = this
else
// NOTE/ASSUMPTION: EVP_PKEY_keygen, EVP_PKEY_generate assume only other possibilities,
// each take in a CTX as the first arg, need to trace from an alg sink from this CTX param
// get every alg sink, get the corresponding call, trace out on any CTX type variable
// to the key gen
// NOTE: looking for any cryptographic algorithm tracing to the keygen to handle
// any odd cases we aren't awaare of where keygen can be used for other algorithm types
exists(DataFlow::Node src, DataFlow::Node sink |
EVP_PKEY_CTX_Ptr_Source_to_KeyGenOperationWithNoSize_Flow::flow(src, sink) and
isEVP_PKEY_CTX_Source(src, result) and
isKeyGen_EVP_PKEY_CTX_Sink(sink, this)
// TODO: what if there is no CTX source? then the keygen becomes an UNKNOWN sink
)
}
/**
* For this class, there is no known configuration source for any algorithm
*/
override Expr getKeyConfigurationSource(CryptographicAlgorithm alg) { none() }
}
}

View File

@@ -1,296 +0,0 @@
/**
* Predicates/classes for identifying algorithm sinks.
* An Algorithm Sink is a function that takes an algorithm as an argument.
* In particular, any function that takes in an algorithm that until the call
* the algorithm is not definitely known to be an algorithm (e.g., an integer used as an identifier to fetch an algorithm)
*/
//TODO: enforce a hierarchy of AlgorithmSinkArgument, e.g., so I can get all Asymmetric SinkArguments that includes all the strictly RSA etc.
import cpp
import experimental.cryptography.utils.OpenSSL.LibraryFunction
import experimental.cryptography.CryptoAlgorithmNames
predicate isAlgorithmSink(AlgorithmSinkArgument arg, string algType) { arg.algType() = algType }
abstract class AlgorithmSinkArgument extends Expr {
AlgorithmSinkArgument() {
exists(Call c | c.getAnArgument() = this and openSSLLibraryFunc(c.getTarget()))
}
/**
* Gets the function call in which the argument exists
*/
Call getSinkCall() { result.getAnArgument() = this }
abstract string algType();
}
// https://www.openssl.org/docs/manmaster/man3/EVP_CIPHER_fetch.html
predicate cipherAlgorithmSink(string funcName, int argInd) {
funcName in ["EVP_get_cipherbyname", "EVP_get_cipherbynid", "EVP_get_cipherbyobj"] and argInd = 0
or
funcName = "EVP_CIPHER_fetch" and argInd = 1
}
class CipherAlgorithmSink extends AlgorithmSinkArgument {
CipherAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
cipherAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getSymmetricEncryptionType() }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_MAC_fetch
predicate macAlgorithmSink(string funcName, int argInd) {
(funcName = "EVP_MAC_fetch" and argInd = 1)
}
class MACAlgorithmSink extends AlgorithmSinkArgument {
MACAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
macAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = "TBD" }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_MD_fetch
predicate messageDigestAlgorithmSink(string funcName, int argInd) {
funcName in ["EVP_get_digestbyname", "EVP_get_digestbynid", "EVP_get_digestbyobj"] and argInd = 0
or
funcName = "EVP_MD_fetch" and argInd = 1
}
class MessageDigestAlgorithmSink extends AlgorithmSinkArgument {
MessageDigestAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
messageDigestAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getHashType() }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_KEYEXCH_fetch
// https://www.openssl.org/docs/manmaster/man3/EVP_KEM_fetch
predicate keyExchangeAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_KEYEXCH_fetch" and argInd = 1
or
funcName = "EVP_KEM_fetch" and argInd = 1
}
class KeyExchangeAlgorithmSink extends AlgorithmSinkArgument {
KeyExchangeAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
keyExchangeAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getKeyExchangeType() }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_KEYMGMT_fetch
predicate keyManagementAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_KEYMGMT_fetch" and argInd = 1
}
class KeyManagementAlgorithmSink extends AlgorithmSinkArgument {
KeyManagementAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
keyManagementAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = "TBD" }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_KDF
predicate keyDerivationAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_KDF_fetch" and argInd = 1
}
class KeyDerivationAlgorithmSink extends AlgorithmSinkArgument {
KeyDerivationAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
keyDerivationAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getKeyDerivationType() }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_ASYM_CIPHER_fetch
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new_CMAC_key.html
predicate asymmetricCipherAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_ASYM_CIPHER_fetch" and argInd = 1
or
funcName = "EVP_PKEY_new_CMAC_key" and argInd = 3
// NOTE: other cases are handled by AsymmetricAlgorithmSink
}
class AsymmetricCipherAlgorithmSink extends AlgorithmSinkArgument {
AsymmetricCipherAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
asymmetricCipherAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = "ASYMMETRIC_ENCRYPTION" }
}
class AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
AsymmetricCipherAlgorithmSink_EVP_PKEY_Q_keygen() {
exists(Call c, string funcName |
funcName = c.getTarget().getName() and
this = c.getArgument(3)
|
funcName = "EVP_PKEY_Q_keygen" and
c.getArgument(3).getType().getUnderlyingType() instanceof IntegralType
)
}
override string algType() { result = "ASYMMETRIC_ENCRYPTION" }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_RAND_fetch
predicate randomAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_RAND_fetch" and argInd = 1
}
class RandomAlgorithmSink extends AlgorithmSinkArgument {
RandomAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
randomAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = "TBD" }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_SIGNATURE_fetch
predicate signatureAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_SIGNATURE_fetch" and argInd = 1
}
class SignatureAlgorithmSink extends AlgorithmSinkArgument {
SignatureAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
signatureAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getSignatureType() }
}
// https://www.openssl.org/docs/manmaster/man3/EC_KEY_new_by_curve_name.html
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_set_ec_paramgen_curve_nid.html
predicate ellipticCurveAlgorithmSink(string funcName, int argInd) {
funcName in ["EC_KEY_new_by_curve_name", "EVP_EC_gen"] and argInd = 0
or
funcName = "EC_KEY_new_by_curve_name_ex" and argInd = 2
or
funcName in ["EVP_PKEY_CTX_set_ec_paramgen_curve_nid"] and argInd = 1
}
class EllipticCurveAlgorithmSink extends AlgorithmSinkArgument {
EllipticCurveAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
ellipticCurveAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getEllipticCurveType() }
}
/**
* Special cased to address the fact that arg index 3 (zero offset based) is the curve name.
* ASSUMPTION: if the arg ind 3 is a char* assume it is an elliptic curve
*/
class EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
EllipticCurveAlgorithmSink_EVP_PKEY_Q_keygen() {
exists(Call c, string funcName |
funcName = c.getTarget().getName() and
this = c.getArgument(3)
|
funcName = "EVP_PKEY_Q_keygen" and
c.getArgument(3).getType().getUnderlyingType() instanceof PointerType and
c.getArgument(3).getType().getUnderlyingType().stripType() instanceof CharType
)
}
override string algType() { result = getEllipticCurveType() }
}
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_new_id.html
// https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_new_raw_private_key.html
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_new.html
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_Q_keygen.html
// https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_ctrl.html
predicate asymmetricAlgorithmSink(string funcName, int argInd) {
funcName = "EVP_PKEY_CTX_new_id" and argInd = 0
or
funcName = "EVP_PKEY_CTX_new_from_name" and argInd = 1
or
funcName in [
"EVP_PKEY_new_raw_private_key", "EVP_PKEY_new_raw_public_key", "EVP_PKEY_new_mac_key"
] and
argInd = 0
or
funcName in ["EVP_PKEY_new_raw_private_key_ex", "EVP_PKEY_new_raw_public_key_ex"] and argInd = 1
or
// special casing this as arg index 3 must be specified depending on if RSA or ECC, and otherwise not specified for other algs
// funcName = "EVP_PKEY_Q_keygen" and argInd = 2
funcName in ["EVP_PKEY_CTX_ctrl", "EVP_PKEY_CTX_set_group_name"] and argInd = 1
// TODO consider void cases EVP_PKEY_new
}
class AsymmetricAlgorithmSink extends AlgorithmSinkArgument {
AsymmetricAlgorithmSink() {
exists(Call c, string funcName, int argInd |
funcName = c.getTarget().getName() and this = c.getArgument(argInd)
|
asymmetricAlgorithmSink(funcName, argInd)
)
}
override string algType() { result = getAsymmetricType() }
}
class AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen extends AlgorithmSinkArgument {
AsymmetricAlgorithmSink_EVP_PKEY_Q_keygen() {
exists(Call c, string funcName |
funcName = c.getTarget().getName() and
this = c.getArgument(2)
|
funcName = "EVP_PKEY_Q_keygen" and
not exists(c.getArgument(3))
)
}
override string algType() { result = getAsymmetricType() }
}

View File

@@ -1,121 +0,0 @@
import cpp
import experimental.cryptography.utils.OpenSSL.LibraryFunction
import experimental.cryptography.CryptoAlgorithmNames
predicate inferredOpenSSLCryptoFunctionCall(Call c, string normalized, string algType) {
inferredOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
}
predicate inferredOpenSSLCryptoFunction(Function f, string normalized, string algType) {
isPossibleOpenSSLFunction(f) and
normalizeFunctionName(f, algType) = normalized
}
predicate isOpenSSLCryptoFunction(Function f, string normalized, string algType) {
// NOTE: relying on inference as there are thousands of functions for crypto
// enumerating them all and maintaining the list seems problematic.
// For now, we will rely on dynamically inferring algorithms for function names.
// This has been seen to be reasonably efficient and accurate.
inferredOpenSSLCryptoFunction(f, normalized, algType)
}
predicate isOpenSSLCryptoFunctionCall(Call c, string normalized, string algType) {
isOpenSSLCryptoFunction(c.getTarget(), normalized, algType)
}
private string basicNormalizeFunctionName(Function f, string algType) {
isPossibleOpenSSLFunction(f) and
isKnownAlgorithm(result, algType) and
exists(string normStr | normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
normStr.matches("%" + result + "%")
)
}
/**
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
*
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
*
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
*
* `f` is the function name to normalize.
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
*/
private string privateNormalizeFunctionName(Function f, string algType) {
isPossibleOpenSSLFunction(f) and
result = basicNormalizeFunctionName(f, algType) and
not exists(string res2 |
result != res2 and
res2 = basicNormalizeFunctionName(f, algType) and
res2.length() > result.length()
)
}
/**
* Normalizes a function name to a known algorithm name, similar to `normalizeName`.
* A function is not, however, allowed to be UNKNOWN. The function either
* normalizes to a known algorithm name, or the predicate does not hold (no result).
*
* The predicate attempts to restrict normalization to what looks like an openssl
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
* This may give false postive functions if a directory erronously appears to be openssl;
* however, we take the stance that if a function
* exists strongly mapping to a known function name in a directory such as these,
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.
*/
private string normalizeFunctionName(Function f, string algType) {
algType != "UNKNOWN" and
isPossibleOpenSSLFunction(f) and
result = privateNormalizeFunctionName(f, algType) and
// Addressing false positives
// For algorithm names less than or equal to 4, we must see the algorithm name
// in the original function as upper case (it can't be split between tokens)
// One exception found is DES_xcbc_encrypt, this is DESX
(
(result.length() <= 4 and result != "DESX")
implies
f.getName().toUpperCase().matches("%" + result + "%")
) and
(
(result.length() <= 4 and result = "DESX")
implies
(f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%"))
) and
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
// and
// DES specific false positives
(
result.matches("DES")
implies
not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")
) and
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
// f.getName().toUpperCase().matches("%" + s + "%"))) and
// SEED specific false positives
(
result.matches("SEED")
implies
not exists(string s |
s in [
"SEED_SRC_GENERATE", "RAND", "NEW_SEED", "GEN_SEED", "SEED_GEN", "SET_SEED", "GET_SEED",
"GET0_SEED", "RESEED", "SEEDING"
]
|
f.getName().toUpperCase().matches("%" + s + "%")
)
) and
// ARIA specific false positives
(result.matches("ARIA") implies not f.getName().toUpperCase().matches("%VARIANT%")) and
// CTR false positives
(result.matches("CTR") implies not f.getName().toUpperCase().matches("%CTRL%")) and
// ES false positives (e.g., ES256 from AES256)
(result.matches("ES%") implies not f.getName().toUpperCase().matches("%AES%")) and
// RSA false positives
(result.matches("RSA") implies not f.getName().toUpperCase().matches("%UNIVERSAL%")) and
//rsaz functions deemed to be too low level, and can be ignored
not f.getLocation().getFile().getBaseName().matches("rsaz_exp.c") and
// General False positives
// Functions that 'get' do not set an algorithm, and therefore are considered ignorable
not f.getName().toLowerCase().matches("%get%")
}

View File

@@ -1,153 +0,0 @@
/**
* This file contains predicates create to build up initial data sets for OpenSSL
* predicates. E.g., These predicates were used to assist in associating all
* openSSL functions with their known crypto algorithms.
*/
import cpp
import experimental.cryptography.CryptoAlgorithmNames
import experimental.cryptography.utils.OpenSSL.CryptoFunction
private string basicNormalizeFunctionName(Function f, string algType) {
isKnownAlgorithm(result, algType) and
exists(string normStr | normStr = f.getName().toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
normStr.matches("%" + result + "%")
)
}
/**
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
*
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
*
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
*
* `f` is the function name to normalize.
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
*/
private string privateNormalizeFunctionName(Function f, string algType) {
result = basicNormalizeFunctionName(f, algType) and
not exists(string res2 |
result != res2 and
res2 = basicNormalizeFunctionName(f, algType) and
res2.length() > result.length()
) and
// Addressing bad normalization case-by-case
// CASE: ES256 being identified when the algorithm is AES256
(
result.matches("ES256")
implies
not exists(string res2 | res2 = basicNormalizeFunctionName(f, _) and res2.matches("AES%"))
)
}
/**
* Normalizes a function name to a known algorithm name, similar to `normalizeName`.
* A function is not, however, allowed to be UNKNOWN. The function either
* normalizes to a known algorithm name, or the predicate does not hold (no result).
*
* The predicate attempts to restrict normalization to what looks like an openssl
* library by looking for functions only in an openssl path (see `isPossibleOpenSSLFunction`).
* This may give false postive functions if a directory erronously appears to be openssl;
* however, we take the stance that if a function
* exists strongly mapping to a known function name in a directory such as these,
* regardless of whether its actually a part of openSSL or not, we will analyze it as though it were.
*/
string normalizeFunctionName(Function f, string algType) {
algType != "UNKNOWN" and
result = privateNormalizeFunctionName(f, algType) and
openSSLLibraryFunc(f) and
// Addressing false positives
// For algorithm names less than or equal to 4, we must see the algorithm name
// in the original function as upper case (it can't be split between tokens)
// One exception found is DES_xcbc_encrypt, this is DESX
(
(result.length() <= 4 and result != "DESX")
implies
f.getName().toUpperCase().matches("%" + result + "%")
) and
(
(result.length() <= 4 and result = "DESX")
implies
(f.getName().toUpperCase().matches("%DESX%") or f.getName().toUpperCase().matches("%DES_X%"))
) and
// (result.length() <= 3 implies (not f.getName().toUpperCase().regexpMatch(".*" + result + "[a-zA-Z0-9].*|.*[a-zA-Z0-9]" + result + ".*")))
// and
// DES specific false positives
(
result.matches("DES")
implies
not f.getName().toUpperCase().regexpMatch(".*DES[a-zA-Z0-9].*|.*[a-zA-Z0-9]DES.*")
) and
// ((result.matches("%DES%")) implies not exists(string s | s in ["DESCRIBE", "DESTROY", "DESCRIPTION", "DESCRIPTOR", "NODES"] |
// f.getName().toUpperCase().matches("%" + s + "%"))) and
// SEED specific false positives
(
result.matches("%SEED%")
implies
not not exists(string s |
s in ["NEW_SEED", "GEN_SEED", "SET_SEED", "GET_SEED", "GET0_SEED", "RESEED", "SEEDING"]
|
f.getName().toUpperCase().matches("%" + s + "%")
)
) and
// ARIA specific false positives
(result.matches("%ARIA%") implies not f.getName().toUpperCase().matches("%VARIANT%"))
}
/**
* Predicate to support name normalization.
* Converts the raw name upper-case with no hyphen, slash, underscore, hash, or space.
* Looks for substrings that are known algorithms, and normalizes the name.
* If the algorithm cannot be determined or is in the ignorable list (`isIgnorableOpenSSLAlgorithm`)
* this predicate will not resolve a name.
*
* Rationale for private: For normalization, we want to get the longest string for a normalized name match
* for a given algorithm type. I found this easier to express if the public normalizeName
* checks that the name is the longest, and that UNKNOWN is reserved if there exists no
* result from this predicate that is known.
*/
bindingset[name]
string privateNormalizeName(string name, string algType) {
//not isIgnorableOpenSSLAlgorithm(name, _, _) and
// targetOpenSSLAlgorithm(name, _) and
isKnownAlgorithm(result, algType) and
exists(string normStr | normStr = name.toUpperCase().regexpReplaceAll("[-_ ]|/", "") |
normStr.matches("%" + result + "%")
)
}
/**
* Converts a raw OpenSSL algorithm to a normalized algorithm name.
*
* If more than one match occurs for a given algorithm type, normalize attempts to find the "max"
* string (max in terms of string length) e.g., matching AES128 to AES128 and not simply AES.
*
* An unknown algorithm is only identified if there exists no known algorithm found for any algorithm type.
*
* `name` is the name to normalize.
* `algType` is a string representing the classification of the algorithm (see `CryptoAlgorithmNames`)
*/
bindingset[name]
string normalizeName(string name, string algType) {
(
if exists(privateNormalizeName(name, _))
then result = privateNormalizeName(name, algType)
else (
result = unknownAlgorithm() and algType = "UNKNOWN"
)
) and
not exists(string res2 |
result != res2 and
res2 = privateNormalizeName(name, algType) and
res2.length() > result.length()
) and
// Addressing bad normalization case-by-case
// CASE: ES256 being identified when the algorithm is AES256
(
result.matches("ES256")
implies
not exists(string res2 | res2 = privateNormalizeName(name, _) and res2.matches("AES%"))
)
}

View File

@@ -1,59 +0,0 @@
import cpp
import experimental.cryptography.utils.OpenSSL.LibraryFunction
import semmle.code.cpp.ir.dataflow.DataFlow
// TODO: possible use of extensible predicates here
// NOTE: -1 for outInd represents the return value
predicate knownPassthroughFunction(Function f, int inInd, int outInd) {
// Trace through functions
// See https://www.openssl.org/docs/man1.1.1/man3/OBJ_obj2txt
// https://www.openssl.org/docs/man3.0/man3/EVP_CIPHER_get0_name
openSSLLibraryFunc(f) and
(
f.getName() in [
"OBJ_nid2obj", "OBJ_nid2ln", "OBJ_nid2sn", "OBJ_obj2nid", "OBJ_ln2nid", "OBJ_sn2nid",
"OBJ_txt2nid", "OBJ_txt2obj", "OBJ_dup", "EVP_CIPHER_get0_name"
] and
inInd = 0 and
outInd = -1
or
f.getName() in ["OBJ_obj2txt", "i2t_ASN1_OBJECT"] and
inInd = 2 and
outInd = 0
or
// Dup/copy pattern occurs in more places,
//see: https://www.openssl.org/docs/manmaster/man3/EC_KEY_copy.html and https://www.openssl.org/docs/manmaster/man3/EVP_PKEY_CTX_dup.html
f.getName().matches("%_dup") and inInd = 0 and outInd = -1
or
f.getName().matches("%_copy") and inInd = 0 and outInd = -1
)
}
/**
* `c` is a call to a function that preserves the algorithm but changes its form.
* `onExpr` is the input argument passing through to, `outExpr` is the next expression in a dataflow step associated with `c`
*/
predicate knownPassthoughCall(Call c, Expr inExpr, Expr outExpr) {
exists(int inInd, int outInd |
knownPassthroughFunction(c.getTarget(), inInd, outInd) and
inExpr = c.getArgument(inInd) and
if outInd = -1 then outExpr = c else outExpr = c.getArgument(outInd)
)
}
/*
* Explicitly add flow through openssl functions that preserve the algorithm but alter the form (e.g., from NID to string)
*/
predicate knownPassThroughStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(Expr cur, Expr next |
(cur = node1.asExpr() or cur = node1.asIndirectArgument()) and
(
next = node2.asExpr() or
next = node2.asIndirectArgument() or
next = node2.asDefiningArgument()
)
|
exists(Call c | knownPassthoughCall(c, cur, next))
)
}

View File

@@ -0,0 +1,395 @@
import semmle.code.cpp.ir.dataflow.DataFlow
private import codeql.util.Unit
module ProductFlow {
signature module ConfigSig {
/**
* Holds if `(source1, source2)` is a relevant data flow source.
*
* `source1` and `source2` must belong to the same callable.
*/
predicate isSourcePair(DataFlow::Node source1, DataFlow::Node source2);
/**
* Holds if `(sink1, sink2)` is a relevant data flow sink.
*
* `sink1` and `sink2` must belong to the same callable.
*/
predicate isSinkPair(DataFlow::Node sink1, DataFlow::Node sink2);
/**
* Holds if data flow through `node` is prohibited through the first projection of the product
* dataflow graph.
*/
default predicate isBarrier1(DataFlow::Node node) { none() }
/**
* Holds if data flow through `node` is prohibited through the second projection of the product
* dataflow graph.
*/
default predicate isBarrier2(DataFlow::Node node) { none() }
/**
* Holds if data flow out of `node` is prohibited in the first projection of the product
* dataflow graph.
*/
default predicate isBarrierOut1(DataFlow::Node node) { none() }
/**
* Holds if data flow out of `node` is prohibited in the second projection of the product
* dataflow graph.
*/
default predicate isBarrierOut2(DataFlow::Node node) { none() }
/*
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the first projection of the product dataflow graph.
*/
default predicate isAdditionalFlowStep1(DataFlow::Node node1, DataFlow::Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the second projection of the product dataflow graph.
*/
default predicate isAdditionalFlowStep2(DataFlow::Node node1, DataFlow::Node node2) { none() }
/**
* Holds if data flow into `node` is prohibited in the first projection of the product
* dataflow graph.
*/
default predicate isBarrierIn1(DataFlow::Node node) { none() }
/**
* Holds if data flow into `node` is prohibited in the second projection of the product
* dataflow graph.
*/
default predicate isBarrierIn2(DataFlow::Node node) { none() }
}
module Global<ConfigSig Config> {
private module StateConfig implements StateConfigSig {
class FlowState1 = Unit;
class FlowState2 = Unit;
predicate isSourcePair(
DataFlow::Node source1, FlowState1 state1, DataFlow::Node source2, FlowState2 state2
) {
exists(state1) and
exists(state2) and
Config::isSourcePair(source1, source2)
}
predicate isSinkPair(
DataFlow::Node sink1, FlowState1 state1, DataFlow::Node sink2, FlowState2 state2
) {
exists(state1) and
exists(state2) and
Config::isSinkPair(sink1, sink2)
}
predicate isBarrier1(DataFlow::Node node, FlowState1 state) {
exists(state) and
Config::isBarrier1(node)
}
predicate isBarrier2(DataFlow::Node node, FlowState2 state) {
exists(state) and
Config::isBarrier2(node)
}
predicate isBarrier1 = Config::isBarrier1/1;
predicate isBarrier2 = Config::isBarrier2/1;
predicate isBarrierOut1 = Config::isBarrierOut1/1;
predicate isBarrierOut2 = Config::isBarrierOut2/1;
predicate isAdditionalFlowStep1 = Config::isAdditionalFlowStep1/2;
predicate isAdditionalFlowStep1(
DataFlow::Node node1, FlowState1 state1, DataFlow::Node node2, FlowState1 state2
) {
exists(state1) and
exists(state2) and
Config::isAdditionalFlowStep1(node1, node2)
}
predicate isAdditionalFlowStep2 = Config::isAdditionalFlowStep2/2;
predicate isAdditionalFlowStep2(
DataFlow::Node node1, FlowState2 state1, DataFlow::Node node2, FlowState2 state2
) {
exists(state1) and
exists(state2) and
Config::isAdditionalFlowStep2(node1, node2)
}
predicate isBarrierIn1 = Config::isBarrierIn1/1;
predicate isBarrierIn2 = Config::isBarrierIn2/1;
}
import GlobalWithState<StateConfig>
}
signature module StateConfigSig {
bindingset[this]
class FlowState1;
bindingset[this]
class FlowState2;
/**
* Holds if `(source1, source2)` is a relevant data flow source with initial states `state1`
* and `state2`, respectively.
*
* `source1` and `source2` must belong to the same callable.
*/
predicate isSourcePair(
DataFlow::Node source1, FlowState1 state1, DataFlow::Node source2, FlowState2 state2
);
/**
* Holds if `(sink1, sink2)` is a relevant data flow sink with final states `state1`
* and `state2`, respectively.
*
* `sink1` and `sink2` must belong to the same callable.
*/
predicate isSinkPair(
DataFlow::Node sink1, FlowState1 state1, DataFlow::Node sink2, FlowState2 state2
);
/**
* Holds if data flow through `node` is prohibited through the first projection of the product
* dataflow graph when the flow state is `state`.
*/
predicate isBarrier1(DataFlow::Node node, FlowState1 state);
/**
* Holds if data flow through `node` is prohibited through the second projection of the product
* dataflow graph when the flow state is `state`.
*/
predicate isBarrier2(DataFlow::Node node, FlowState2 state);
/**
* Holds if data flow through `node` is prohibited through the first projection of the product
* dataflow graph.
*/
default predicate isBarrier1(DataFlow::Node node) { none() }
/**
* Holds if data flow through `node` is prohibited through the second projection of the product
* dataflow graph.
*/
default predicate isBarrier2(DataFlow::Node node) { none() }
/**
* Holds if data flow out of `node` is prohibited in the first projection of the product
* dataflow graph.
*/
default predicate isBarrierOut1(DataFlow::Node node) { none() }
/**
* Holds if data flow out of `node` is prohibited in the second projection of the product
* dataflow graph.
*/
default predicate isBarrierOut2(DataFlow::Node node) { none() }
/*
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the first projection of the product dataflow graph.
*/
default predicate isAdditionalFlowStep1(DataFlow::Node node1, DataFlow::Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the first projection of the product dataflow graph.
*
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep1(
DataFlow::Node node1, FlowState1 state1, DataFlow::Node node2, FlowState1 state2
);
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the second projection of the product dataflow graph.
*/
default predicate isAdditionalFlowStep2(DataFlow::Node node1, DataFlow::Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the second projection of the product dataflow graph.
*
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep2(
DataFlow::Node node1, FlowState2 state1, DataFlow::Node node2, FlowState2 state2
);
/**
* Holds if data flow into `node` is prohibited in the first projection of the product
* dataflow graph.
*/
default predicate isBarrierIn1(DataFlow::Node node) { none() }
/**
* Holds if data flow into `node` is prohibited in the second projection of the product
* dataflow graph.
*/
default predicate isBarrierIn2(DataFlow::Node node) { none() }
}
module GlobalWithState<StateConfigSig Config> {
class PathNode1 = Flow1::PathNode;
class PathNode2 = Flow2::PathNode;
module PathGraph1 = Flow1::PathGraph;
module PathGraph2 = Flow2::PathGraph;
class FlowState1 = Config::FlowState1;
class FlowState2 = Config::FlowState2;
predicate flowPath(
Flow1::PathNode source1, Flow2::PathNode source2, Flow1::PathNode sink1, Flow2::PathNode sink2
) {
reachable(source1, source2, sink1, sink2)
}
private module Config1 implements DataFlow::StateConfigSig {
class FlowState = FlowState1;
predicate isSource(DataFlow::Node source, FlowState state) {
Config::isSourcePair(source, state, _, _)
}
predicate isSink(DataFlow::Node sink, FlowState state) {
Config::isSinkPair(sink, state, _, _)
}
predicate isBarrier(DataFlow::Node node, FlowState state) { Config::isBarrier1(node, state) }
predicate isBarrierOut(DataFlow::Node node) { Config::isBarrierOut1(node) }
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState1 state1, DataFlow::Node node2, FlowState state2
) {
Config::isAdditionalFlowStep1(node1, state1, node2, state2)
}
predicate isBarrierIn(DataFlow::Node node) { Config::isBarrierIn1(node) }
}
module Flow1 = DataFlow::GlobalWithState<Config1>;
module Config2 implements DataFlow::StateConfigSig {
class FlowState = FlowState2;
predicate isSource(DataFlow::Node source, FlowState state) {
exists(Flow1::PathNode source1 |
Config::isSourcePair(source1.getNode(), source1.getState(), source, state) and
Flow1::flowPath(source1, _)
)
}
predicate isSink(DataFlow::Node sink, FlowState state) {
exists(Flow1::PathNode sink1 |
Config::isSinkPair(sink1.getNode(), sink1.getState(), sink, state) and
Flow1::flowPath(_, sink1)
)
}
predicate isBarrier(DataFlow::Node node, FlowState state) { Config::isBarrier2(node, state) }
predicate isBarrierOut(DataFlow::Node node) { Config::isBarrierOut2(node) }
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
Config::isAdditionalFlowStep2(node1, state1, node2, state2)
}
predicate isBarrierIn(DataFlow::Node node) { Config::isBarrierIn2(node) }
}
module Flow2 = DataFlow::GlobalWithState<Config2>;
pragma[nomagic]
private predicate reachableInterprocEntry(
Flow1::PathNode source1, Flow2::PathNode source2, Flow1::PathNode node1, Flow2::PathNode node2
) {
Config::isSourcePair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState()) and
node1 = source1 and
node2 = source2
or
exists(
Flow1::PathNode midEntry1, Flow2::PathNode midEntry2, Flow1::PathNode midExit1,
Flow2::PathNode midExit2
|
reachableInterprocEntry(source1, source2, midEntry1, midEntry2) and
interprocEdgePair(midExit1, midExit2, node1, node2) and
localPathStep1*(midEntry1, midExit1) and
localPathStep2*(midEntry2, midExit2)
)
}
private predicate localPathStep1(Flow1::PathNode pred, Flow1::PathNode succ) {
Flow1::PathGraph::edges(pred, succ) and
pragma[only_bind_out](pred.getNode().getEnclosingCallable()) =
pragma[only_bind_out](succ.getNode().getEnclosingCallable())
}
private predicate localPathStep2(Flow2::PathNode pred, Flow2::PathNode succ) {
Flow2::PathGraph::edges(pred, succ) and
pragma[only_bind_out](pred.getNode().getEnclosingCallable()) =
pragma[only_bind_out](succ.getNode().getEnclosingCallable())
}
pragma[nomagic]
private predicate interprocEdge1(
Declaration predDecl, Declaration succDecl, Flow1::PathNode pred1, Flow1::PathNode succ1
) {
Flow1::PathGraph::edges(pred1, succ1) and
predDecl != succDecl and
pred1.getNode().getEnclosingCallable() = predDecl and
succ1.getNode().getEnclosingCallable() = succDecl
}
pragma[nomagic]
private predicate interprocEdge2(
Declaration predDecl, Declaration succDecl, Flow2::PathNode pred2, Flow2::PathNode succ2
) {
Flow2::PathGraph::edges(pred2, succ2) and
predDecl != succDecl and
pred2.getNode().getEnclosingCallable() = predDecl and
succ2.getNode().getEnclosingCallable() = succDecl
}
private predicate interprocEdgePair(
Flow1::PathNode pred1, Flow2::PathNode pred2, Flow1::PathNode succ1, Flow2::PathNode succ2
) {
exists(Declaration predDecl, Declaration succDecl |
interprocEdge1(predDecl, succDecl, pred1, succ1) and
interprocEdge2(predDecl, succDecl, pred2, succ2)
)
}
private predicate reachable(
Flow1::PathNode source1, Flow2::PathNode source2, Flow1::PathNode sink1, Flow2::PathNode sink2
) {
exists(Flow1::PathNode mid1, Flow2::PathNode mid2 |
reachableInterprocEntry(source1, source2, mid1, mid2) and
Config::isSinkPair(sink1.getNode(), sink1.getState(), sink2.getNode(), sink2.getState()) and
localPathStep1*(mid1, sink1) and
localPathStep2*(mid2, sink2)
)
}
}
}

View File

@@ -238,7 +238,7 @@ class NoReason extends Reason, TNoReason {
class CondReason extends Reason, TCondReason {
IRGuardCondition getCond() { this = TCondReason(result) }
override string toString() { result = this.getCond().toString() }
override string toString() { result = getCond().toString() }
}
/**
@@ -260,14 +260,14 @@ private predicate typeBound(IRIntegerType typ, int lowerbound, int upperbound) {
private class NarrowingCastInstruction extends ConvertInstruction {
NarrowingCastInstruction() {
not this instanceof SafeCastInstruction and
typeBound(this.getResultIRType(), _, _)
typeBound(getResultIRType(), _, _)
}
/** Gets the lower bound of the resulting type. */
int getLowerBound() { typeBound(this.getResultIRType(), result, _) }
int getLowerBound() { typeBound(getResultIRType(), result, _) }
/** Gets the upper bound of the resulting type. */
int getUpperBound() { typeBound(this.getResultIRType(), _, result) }
int getUpperBound() { typeBound(getResultIRType(), _, result) }
}
/**

View File

@@ -109,8 +109,8 @@ private predicate safeCast(IRIntegerType fromtyp, IRIntegerType totyp) {
*/
class PtrToPtrCastInstruction extends ConvertInstruction {
PtrToPtrCastInstruction() {
this.getResultIRType() instanceof IRAddressType and
this.getUnary().getResultIRType() instanceof IRAddressType
getResultIRType() instanceof IRAddressType and
getUnary().getResultIRType() instanceof IRAddressType
}
}
@@ -119,7 +119,7 @@ class PtrToPtrCastInstruction extends ConvertInstruction {
* that cannot overflow or underflow.
*/
class SafeIntCastInstruction extends ConvertInstruction {
SafeIntCastInstruction() { safeCast(this.getUnary().getResultIRType(), this.getResultIRType()) }
SafeIntCastInstruction() { safeCast(getUnary().getResultIRType(), getResultIRType()) }
}
/**

View File

@@ -50,8 +50,8 @@ private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
// If an operand can have negative values, the lower bound is unconstrained.
// Otherwise, the lower bound is zero.
exists(float lLower, float rLower |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
(
(lLower < 0 or rLower < 0) and
result = exprMinVal(this)
@@ -68,10 +68,10 @@ private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
// If an operand can have negative values, the upper bound is unconstrained.
// Otherwise, the upper bound is the minimum of the upper bounds of the operands
exists(float lLower, float lUpper, float rLower, float rUpper |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
(
(lLower < 0 or rLower < 0) and
result = exprMaxVal(this)
@@ -85,6 +85,6 @@ private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
}
override predicate dependsOnChild(Expr child) {
child = this.getLeftOperand() or child = this.getRightOperand()
child = getLeftOperand() or child = getRightOperand()
}
}

View File

@@ -50,7 +50,7 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
* We don't handle the case where `a` and `b` are both non-constant values.
*/
ConstantRShiftExprRange() {
this.getUnspecifiedType() instanceof IntegralType and
getUnspecifiedType() instanceof IntegralType and
exists(Expr l, Expr r |
l = this.(RShiftExpr).getLeftOperand() and
r = this.(RShiftExpr).getRightOperand()
@@ -84,10 +84,10 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
override float getLowerBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
@@ -95,8 +95,8 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
lLower < 0
or
not (
isValidShiftExprShift(rLower, this.getLeftOperand()) and
isValidShiftExprShift(rUpper, this.getLeftOperand())
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
@@ -111,10 +111,10 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
override float getUpperBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
@@ -122,8 +122,8 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
lLower < 0
or
not (
isValidShiftExprShift(rLower, this.getLeftOperand()) and
isValidShiftExprShift(rUpper, this.getLeftOperand())
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
@@ -137,7 +137,7 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
}
override predicate dependsOnChild(Expr child) {
child = this.getLeftOperand() or child = this.getRightOperand()
child = getLeftOperand() or child = getRightOperand()
}
}
@@ -163,7 +163,7 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
* We don't handle the case where `a` and `b` are both non-constant values.
*/
ConstantLShiftExprRange() {
this.getUnspecifiedType() instanceof IntegralType and
getUnspecifiedType() instanceof IntegralType and
exists(Expr l, Expr r |
l = this.(LShiftExpr).getLeftOperand() and
r = this.(LShiftExpr).getRightOperand()
@@ -197,10 +197,10 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
override float getLowerBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
@@ -208,8 +208,8 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
lLower < 0
or
not (
isValidShiftExprShift(rLower, this.getLeftOperand()) and
isValidShiftExprShift(rUpper, this.getLeftOperand())
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
@@ -228,10 +228,10 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
override float getUpperBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
@@ -239,8 +239,8 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
lLower < 0
or
not (
isValidShiftExprShift(rLower, this.getLeftOperand()) and
isValidShiftExprShift(rUpper, this.getLeftOperand())
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
@@ -258,6 +258,6 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
}
override predicate dependsOnChild(Expr child) {
child = this.getLeftOperand() or child = this.getRightOperand()
child = getLeftOperand() or child = getRightOperand()
}
}

View File

@@ -83,23 +83,20 @@ private class ExprRangeNode extends DataFlow::ExprNode {
private string getCallBounds(Call e) {
result =
getExprBoundAsString(e) + "(" +
concat(Expr arg, int i |
arg = e.getArgument(i)
|
this.getIntegralBounds(arg), "," order by i
) + ")"
concat(Expr arg, int i | arg = e.getArgument(i) | getIntegralBounds(arg) order by i, ",") +
")"
}
override string toString() {
exists(Expr e | e = this.getExpr() |
exists(Expr e | e = getExpr() |
if hasIntegralOrReferenceIntegralType(e)
then
result = super.toString() + ": " + this.getOperationBounds(e)
result = super.toString() + ": " + getOperationBounds(e)
or
result = super.toString() + ": " + this.getCallBounds(e)
result = super.toString() + ": " + getCallBounds(e)
or
not exists(this.getOperationBounds(e)) and
not exists(this.getCallBounds(e)) and
not exists(getOperationBounds(e)) and
not exists(getCallBounds(e)) and
result = super.toString() + ": " + getExprBoundAsString(e)
else result = super.toString()
)
@@ -111,8 +108,8 @@ private class ExprRangeNode extends DataFlow::ExprNode {
*/
private class ReferenceArgumentRangeNode extends DataFlow::DefinitionByReferenceNode {
override string toString() {
if hasIntegralOrReferenceIntegralType(this.asDefiningArgument())
then result = super.toString() + ": " + getExprBoundAsString(this.getArgument())
if hasIntegralOrReferenceIntegralType(asDefiningArgument())
then result = super.toString() + ": " + getExprBoundAsString(getArgument())
else result = super.toString()
}
}

View File

@@ -7,12 +7,12 @@ private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysi
*/
class StrlenLiteralRangeExpr extends SimpleRangeAnalysisExpr, FunctionCall {
StrlenLiteralRangeExpr() {
this.getTarget().hasGlobalOrStdName("strlen") and this.getArgument(0).isConstant()
getTarget().hasGlobalOrStdName("strlen") and getArgument(0).isConstant()
}
override int getLowerBounds() { result = this.getArgument(0).getValue().length() }
override int getLowerBounds() { result = getArgument(0).getValue().length() }
override int getUpperBounds() { result = this.getArgument(0).getValue().length() }
override int getUpperBounds() { result = getArgument(0).getValue().length() }
override predicate dependsOnChild(Expr e) { none() }
}

View File

@@ -3,8 +3,8 @@ import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
private class SelfSub extends SimpleRangeAnalysisExpr, SubExpr {
SelfSub() {
// Match `x - x` but not `myInt - (unsigned char)myInt`.
this.getLeftOperand().getExplicitlyConverted().(VariableAccess).getTarget() =
this.getRightOperand().getExplicitlyConverted().(VariableAccess).getTarget()
getLeftOperand().getExplicitlyConverted().(VariableAccess).getTarget() =
getRightOperand().getExplicitlyConverted().(VariableAccess).getTarget()
}
override float getLowerBounds() { result = 0 }

View File

@@ -18,10 +18,10 @@ external string selectedSourceFile();
class Cfg extends PrintAstConfiguration {
/**
* Holds if the AST for `decl` should be printed.
* Print All declarations from the selected file.
* Holds if the AST for `func` should be printed.
* Print All functions from the selected file.
*/
override predicate shouldPrintDeclaration(Declaration decl) {
decl.getFile() = getFileBySourceArchiveName(selectedSourceFile())
override predicate shouldPrintFunction(Function func) {
func.getFile() = getFileBySourceArchiveName(selectedSourceFile())
}
}

View File

@@ -1,14 +1,11 @@
name: codeql/cpp-all
version: 0.11.0
version: 0.7.2-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
library: true
upgrades: upgrades
dependencies:
codeql/dataflow: ${workspace}
codeql/rangeanalysis: ${workspace}
codeql/ssa: ${workspace}
codeql/tutorial: ${workspace}
codeql/util: ${workspace}
warnOnImplicitThis: true

View File

@@ -176,6 +176,20 @@ class Class extends UserType {
/** Holds if this class, struct or union has a constructor. */
predicate hasConstructor() { exists(this.getAConstructor()) }
/**
* Holds if this class has a copy constructor that is either explicitly
* declared (though possibly `= delete`) or is auto-generated, non-trivial
* and called from somewhere.
*
* DEPRECATED: There is more than one reasonable definition of what it means
* to have a copy constructor, and we do not want to promote one particular
* definition by naming it with this predicate. Having a copy constructor
* could mean that such a member is declared or defined in the source or that
* it is callable by a particular caller. For C++11, there's also a question
* of whether to include members that are defaulted or deleted.
*/
deprecated predicate hasCopyConstructor() { this.getAMemberFunction() instanceof CopyConstructor }
/**
* Like accessOfBaseMember but returns multiple results if there are multiple
* paths to `base` through the inheritance graph.

View File

@@ -42,7 +42,7 @@ class Compilation extends @compilation {
}
/** Gets a file compiled during this invocation. */
File getAFileCompiled() { result = this.getFileCompiled(_) }
File getAFileCompiled() { result = getFileCompiled(_) }
/** Gets the `i`th file compiled during this invocation */
File getFileCompiled(int i) { compilation_compiling_files(this, i, unresolveElement(result)) }
@@ -74,7 +74,7 @@ class Compilation extends @compilation {
/**
* Gets an argument passed to the extractor on this invocation.
*/
string getAnArgument() { result = this.getArgument(_) }
string getAnArgument() { result = getArgument(_) }
/**
* Gets the `i`th argument passed to the extractor on this invocation.

Some files were not shown because too many files have changed in this diff Show More