mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge branch 'main' into 20823-globalVarRef-document-defaultView
This commit is contained in:
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -82,9 +82,6 @@
|
||||
/csharp/paket.main.bzl linguist-generated=true
|
||||
/csharp/paket.main_extension.bzl linguist-generated=true
|
||||
|
||||
# ripunzip tool
|
||||
/misc/ripunzip/ripunzip-* filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
# swift prebuilt resources
|
||||
/swift/third_party/resources/*.zip filter=lfs diff=lfs merge=lfs -text
|
||||
/swift/third_party/resources/*.tar.zst filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
166
.github/workflows/build-ripunzip.yml
vendored
166
.github/workflows/build-ripunzip.yml
vendored
@@ -1,166 +0,0 @@
|
||||
name: Build ripunzip
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ripunzip-version:
|
||||
description: What reference to checkout from google/ripunzip. Latest by default
|
||||
required: false
|
||||
openssl-version:
|
||||
description: What reference to checkout from openssl/openssl for Linux. Latest by default
|
||||
required: false
|
||||
open-pr:
|
||||
description: Open a pull request updating the ripunzip versions committed to lfs
|
||||
required: false
|
||||
default: true # will be false on PRs
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/build-ripunzip.yml
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
versions:
|
||||
runs-on: ubuntu-slim
|
||||
outputs:
|
||||
ripunzip-version: ${{ inputs.ripunzip-version || steps.fetch-ripunzip-version.outputs.version }}
|
||||
openssl-version: ${{ inputs.openssl-version || steps.fetch-openssl-version.outputs.version }}
|
||||
steps:
|
||||
- name: Fetch latest ripunzip version
|
||||
id: fetch-ripunzip-version
|
||||
if: "!inputs.ripunzip-version"
|
||||
run: &fetch-version
|
||||
echo "version=$(gh release view --repo $REPO --json tagName --jq .tagName)" | tee -a $GITHUB_OUTPUT
|
||||
env:
|
||||
REPO: "google/ripunzip"
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Fetch latest openssl version
|
||||
id: fetch-openssl-version
|
||||
if: "!inputs.openssl-version"
|
||||
run: *fetch-version
|
||||
env:
|
||||
REPO: "openssl/openssl"
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
build:
|
||||
needs: versions
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04, macos-15, windows-2025]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
repository: google/ripunzip
|
||||
ref: ${{ needs.versions.outputs.ripunzip-version }}
|
||||
# we need to avoid ripunzip dynamically linking into libssl
|
||||
# see https://github.com/sfackler/rust-openssl/issues/183
|
||||
- if: runner.os == 'Linux'
|
||||
name: checkout openssl
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: openssl/openssl
|
||||
path: openssl
|
||||
ref: ${{ needs.versions.outputs.openssl-version }}
|
||||
- if: runner.os == 'Linux'
|
||||
name: build and install openssl with fPIC
|
||||
shell: bash
|
||||
working-directory: openssl
|
||||
run: |
|
||||
./config -fPIC --prefix=$HOME/.local --openssldir=$HOME/.local/ssl
|
||||
make -j $(nproc)
|
||||
make install_sw -j $(nproc)
|
||||
- if: runner.os == 'Linux'
|
||||
name: build (linux)
|
||||
shell: bash
|
||||
run: |
|
||||
env OPENSSL_LIB_DIR=$HOME/.local/lib64 OPENSSL_INCLUDE_DIR=$HOME/.local/include OPENSSL_STATIC=yes cargo build --release
|
||||
mv target/release/ripunzip ripunzip-linux
|
||||
- if: runner.os == 'Windows'
|
||||
name: build (windows)
|
||||
shell: bash
|
||||
run: |
|
||||
cargo build --release
|
||||
mv target/release/ripunzip ripunzip-windows
|
||||
- name: build (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
shell: bash
|
||||
run: |
|
||||
rustup target install x86_64-apple-darwin
|
||||
rustup target install aarch64-apple-darwin
|
||||
cargo build --target x86_64-apple-darwin --release
|
||||
cargo build --target aarch64-apple-darwin --release
|
||||
lipo -create -output ripunzip-macos \
|
||||
-arch x86_64 target/x86_64-apple-darwin/release/ripunzip \
|
||||
-arch arm64 target/aarch64-apple-darwin/release/ripunzip
|
||||
- name: Archive
|
||||
shell: bash
|
||||
run: |
|
||||
tar acf ripunzip-$RUNNER_OS.tar.zst ripunzip-$(echo $RUNNER_OS | tr '[:upper:]' '[:lower:]')
|
||||
- name: Upload built binary
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ripunzip-${{ runner.os }}
|
||||
path: ripunzip-${{ runner.os }}.tar.zst
|
||||
retention-days: 5
|
||||
compression: 0
|
||||
- name: Check built binary
|
||||
shell: bash
|
||||
run: |
|
||||
rm -f ripunzip-*.tar.zst
|
||||
./ripunzip-* --version
|
||||
publish:
|
||||
needs: [versions, build]
|
||||
if: inputs.open-pr == 'true'
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
# workaround for git-lfs not being installed yet on ubuntu-slim runners
|
||||
- name: Ensure git-lfs is installed
|
||||
shell: bash
|
||||
run: |
|
||||
if which git-lfs &>/dev/null; then
|
||||
echo "git-lfs is already installed"
|
||||
exit 0
|
||||
fi
|
||||
cd $TMP
|
||||
gh release download --repo git-lfs/git-lfs --pattern "git-lfs-linux-amd64-*.tar.gz" --clobber
|
||||
tar xzf git-lfs-linux-amd64-*.tar.gz
|
||||
rm git-lfs-linux-amd64-*.tar.gz
|
||||
cd git-lfs-*
|
||||
pwd | tee -a $GITHUB_PATH
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
sparse-checkout: |
|
||||
.github
|
||||
misc/ripunzip
|
||||
lfs: true
|
||||
- name: Download built binaries
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
path: misc/ripunzip
|
||||
- name: Open PR
|
||||
shell: bash
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git switch -c update-ripunzip
|
||||
git add misc/ripunzip
|
||||
git commit -m "Update ripunzip binaries to version $VERSION"
|
||||
git push --set-upstream origin update-ripunzip --force
|
||||
TITLE="Update ripunzip binaries to version $VERSION"
|
||||
gh pr create \
|
||||
--draft \
|
||||
--title "$TITLE" \
|
||||
--body "Automated update of ripunzip binaries." \
|
||||
--assignee "$ACTOR" ||
|
||||
(gh pr edit --title "$TITLE" --add-assignee "$ACTOR" && gh pr ready --undo)
|
||||
env:
|
||||
ACTOR: ${{ github.actor }}
|
||||
VERSION: ${{ needs.versions.outputs.ripunzip-version }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
26
MODULE.bazel
26
MODULE.bazel
@@ -269,24 +269,16 @@ go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
|
||||
go_deps.from_file(go_mod = "//go/extractor:go.mod")
|
||||
use_repo(go_deps, "org_golang_x_mod", "org_golang_x_tools")
|
||||
|
||||
lfs_archive = use_repo_rule("//misc/bazel:lfs.bzl", "lfs_archive")
|
||||
ripunzip_archive = use_repo_rule("//misc/ripunzip:ripunzip.bzl", "ripunzip_archive")
|
||||
|
||||
lfs_archive(
|
||||
name = "ripunzip-linux",
|
||||
src = "//misc/ripunzip:ripunzip-Linux.tar.zst",
|
||||
build_file = "//misc/ripunzip:BUILD.ripunzip.bazel",
|
||||
)
|
||||
|
||||
lfs_archive(
|
||||
name = "ripunzip-windows",
|
||||
src = "//misc/ripunzip:ripunzip-Windows.tar.zst",
|
||||
build_file = "//misc/ripunzip:BUILD.ripunzip.bazel",
|
||||
)
|
||||
|
||||
lfs_archive(
|
||||
name = "ripunzip-macos",
|
||||
src = "//misc/ripunzip:ripunzip-macOS.tar.zst",
|
||||
build_file = "//misc/ripunzip:BUILD.ripunzip.bazel",
|
||||
# go to https://github.com/GoogleChrome/ripunzip/releases to find latest version and corresponding sha256s
|
||||
ripunzip_archive(
|
||||
name = "ripunzip",
|
||||
sha256_linux = "ee0e8a957687a5dc3a66b2a4b25883bf762df4c9c07f0651af527a32a405054b",
|
||||
sha256_macos_arm = "8a88eea54eac232d162a72a42065e0429b82dbf4f05e9642915dff9d7a81f846",
|
||||
sha256_macos_intel = "4457a18bfcc5feabe09f5ea3d1157128e07b4873392cb404a870e611924abf64",
|
||||
sha256_windows = "66d0c1375301bf5ab815348048f43b110631d3fa7200acd50d50a8ed8655ca62",
|
||||
version = "2.0.3",
|
||||
)
|
||||
|
||||
register_toolchains(
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.4.21
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.20
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
actions/ql/lib/change-notes/released/0.4.21.md
Normal file
3
actions/ql/lib/change-notes/released/0.4.21.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.4.21
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.20
|
||||
lastReleaseVersion: 0.4.21
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/actions-all
|
||||
version: 0.4.21-dev
|
||||
version: 0.4.22-dev
|
||||
library: true
|
||||
warnOnImplicitThis: true
|
||||
dependencies:
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.6.13
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.12
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
actions/ql/src/change-notes/released/0.6.13.md
Normal file
3
actions/ql/src/change-notes/released/0.6.13.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.13
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.12
|
||||
lastReleaseVersion: 0.6.13
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/actions-queries
|
||||
version: 0.6.13-dev
|
||||
version: 0.6.14-dev
|
||||
library: false
|
||||
warnOnImplicitThis: true
|
||||
groups: [actions, queries]
|
||||
|
||||
2450
cpp/downgrades/1a6854060d5d3ada16c580a29f8c5ce21f3367f8/old.dbscheme
Normal file
2450
cpp/downgrades/1a6854060d5d3ada16c580a29f8c5ce21f3367f8/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
description: Support expanded compilation argument lists
|
||||
compatibility: full
|
||||
compilation_expanded_args.rel: delete
|
||||
@@ -1,3 +1,13 @@
|
||||
## 6.1.0
|
||||
|
||||
### New Features
|
||||
|
||||
* New predicates `getAnExpandedArgument` and `getExpandedArgument` were added to the `Compilation` class, yielding compilation arguments after expansion of response files.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Improve performance of the range analysis in cases where it would otherwise take an exorbitant amount of time.
|
||||
|
||||
## 6.0.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
* Improve performance of the range analysis in cases where it would otherwise take an exorbitant amount of time.
|
||||
4
cpp/ql/lib/change-notes/2025-11-19-content.md
Normal file
4
cpp/ql/lib/change-notes/2025-11-19-content.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The class `DataFlow::FieldContent` now covers both `union` and `struct`/`class` types. A new predicate `FieldContent.getAField` has been added to access the union members associated with the `FieldContent`. The old `FieldContent` has been renamed to `NonUnionFieldContent`.
|
||||
9
cpp/ql/lib/change-notes/released/6.1.0.md
Normal file
9
cpp/ql/lib/change-notes/released/6.1.0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## 6.1.0
|
||||
|
||||
### New Features
|
||||
|
||||
* New predicates `getAnExpandedArgument` and `getExpandedArgument` were added to the `Compilation` class, yielding compilation arguments after expansion of response files.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Improve performance of the range analysis in cases where it would otherwise take an exorbitant amount of time.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 6.0.1
|
||||
lastReleaseVersion: 6.1.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 6.0.2-dev
|
||||
version: 6.1.1-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -94,6 +94,25 @@ class Compilation extends @compilation {
|
||||
*/
|
||||
string getArgument(int i) { compilation_args(this, i, result) }
|
||||
|
||||
/**
|
||||
* Gets an expanded argument passed to the extractor on this invocation.
|
||||
*/
|
||||
string getAnExpandedArgument() { result = this.getExpandedArgument(_) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th expanded argument passed to the extractor on this
|
||||
* invocation.
|
||||
*
|
||||
* This is similar to `getArgument`, but for a `@someFile` argument, it
|
||||
* includes the arguments from that file, rather than just taking the
|
||||
* argument literally.
|
||||
*/
|
||||
string getExpandedArgument(int i) {
|
||||
if exists(string arg | compilation_expanded_args(this, _, arg))
|
||||
then compilation_expanded_args(this, i, result)
|
||||
else result = this.getArgument(i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the total amount of CPU time spent processing all the files in the
|
||||
* front-end and extractor.
|
||||
|
||||
@@ -171,12 +171,14 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
* Gets the nth parameter of this function. There is no result for the
|
||||
* implicit `this` parameter, and there is no `...` varargs pseudo-parameter.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Parameter getParameter(int n) { params(unresolveElement(result), underlyingElement(this), n, _) }
|
||||
|
||||
/**
|
||||
* Gets a parameter of this function. There is no result for the implicit
|
||||
* `this` parameter, and there is no `...` varargs pseudo-parameter.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) }
|
||||
|
||||
/**
|
||||
|
||||
@@ -750,6 +750,16 @@ class SizeofPackTypeOperator extends SizeofPackOperator {
|
||||
*/
|
||||
class SizeofOperator extends Expr, @runtime_sizeof {
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
/**
|
||||
* Gets the contained type of this `sizeof`. For example,
|
||||
* the result is `int` in both cases below:
|
||||
* ```
|
||||
* sizeof(int);
|
||||
* sizeof(42);
|
||||
* ```
|
||||
*/
|
||||
Type getTypeOperand() { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -766,6 +776,8 @@ class SizeofExprOperator extends SizeofOperator {
|
||||
/** Gets the contained expression. */
|
||||
Expr getExprOperand() { result = this.getChild(0) }
|
||||
|
||||
override Type getTypeOperand() { result = this.getExprOperand().getType() }
|
||||
|
||||
override string toString() { result = "sizeof(<expr>)" }
|
||||
|
||||
override predicate mayBeImpure() { this.getExprOperand().mayBeImpure() }
|
||||
@@ -784,8 +796,7 @@ class SizeofTypeOperator extends SizeofOperator {
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SizeofTypeOperator" }
|
||||
|
||||
/** Gets the contained type. */
|
||||
Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) }
|
||||
override Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
override string toString() { result = "sizeof(" + this.getTypeOperand().getName() + ")" }
|
||||
|
||||
@@ -842,6 +853,16 @@ class AlignofTypeOperator extends AlignofOperator {
|
||||
*/
|
||||
class DatasizeofOperator extends Expr, @datasizeof {
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
/**
|
||||
* Gets the contained type of this `__datasizeof`. For example,
|
||||
* the result is `int` in both cases below:
|
||||
* ```
|
||||
* __datasizeof(int);
|
||||
* __datasizeof(42);
|
||||
* ```
|
||||
*/
|
||||
Type getTypeOperand() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -855,6 +876,8 @@ class DatasizeofExprOperator extends DatasizeofOperator {
|
||||
/** Gets the contained expression. */
|
||||
Expr getExprOperand() { result = this.getChild(0) }
|
||||
|
||||
override Type getTypeOperand() { result = this.getExprOperand().getType() }
|
||||
|
||||
override string toString() { result = "__datasizeof(<expr>)" }
|
||||
|
||||
override predicate mayBeImpure() { this.getExprOperand().mayBeImpure() }
|
||||
@@ -870,8 +893,7 @@ class DatasizeofTypeOperator extends DatasizeofOperator {
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DatasizeofTypeOperator" }
|
||||
|
||||
/** Gets the contained type. */
|
||||
Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) }
|
||||
override Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
override string toString() { result = "__datasizeof(" + this.getTypeOperand().getName() + ")" }
|
||||
|
||||
|
||||
@@ -861,6 +861,10 @@ predicate jumpStep(Node n1, Node n2) {
|
||||
n2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
|
||||
bindingset[c]
|
||||
pragma[inline_late]
|
||||
private int getIndirectionIndexLate(Content c) { result = c.getIndirectionIndex() }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
|
||||
* Thus, `node2` references an object with a field `f` that contains the
|
||||
@@ -873,23 +877,17 @@ predicate jumpStep(Node n1, Node n2) {
|
||||
predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) {
|
||||
exists(
|
||||
PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads,
|
||||
StoreInstruction store
|
||||
StoreInstruction store, FieldContent fc
|
||||
|
|
||||
postFieldUpdate = node2 and
|
||||
nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and
|
||||
fc = c and
|
||||
nodeHasInstruction(node1, pragma[only_bind_into](store),
|
||||
pragma[only_bind_into](indirectionIndex1)) and
|
||||
postFieldUpdate.getIndirectionIndex() = 1 and
|
||||
numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(),
|
||||
store.getDestinationAddressOperand(), numberOfLoads, certain)
|
||||
|
|
||||
exists(FieldContent fc | fc = c |
|
||||
fc.getField() = postFieldUpdate.getUpdatedField() and
|
||||
fc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
or
|
||||
exists(UnionContent uc | uc = c |
|
||||
uc.getAField() = postFieldUpdate.getUpdatedField() and
|
||||
uc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
store.getDestinationAddressOperand(), numberOfLoads, certain) and
|
||||
fc.getAField() = postFieldUpdate.getUpdatedField() and
|
||||
getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
@@ -965,22 +963,17 @@ predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex)
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, ContentSet c, Node node2) {
|
||||
exists(FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2 |
|
||||
exists(
|
||||
FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2, FieldContent fc
|
||||
|
|
||||
fc = c and
|
||||
nodeHasOperand(node2, operand, indirectionIndex2) and
|
||||
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
|
||||
// in `storeStep`.
|
||||
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
|
||||
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _)
|
||||
|
|
||||
exists(FieldContent fc | fc = c |
|
||||
fc.getField() = fa1.getField() and
|
||||
fc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
or
|
||||
exists(UnionContent uc | uc = c |
|
||||
uc.getAField() = fa1.getField() and
|
||||
uc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) and
|
||||
fc.getAField() = fa1.getField() and
|
||||
getIndirectionIndexLate(fc) = indirectionIndex2 + numberOfLoads
|
||||
)
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
@@ -1574,7 +1567,7 @@ pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) {
|
||||
exists(string prefix, Field f |
|
||||
prefix = result.(FieldApproxContent).getPrefix() and
|
||||
f = c.(FieldContent).getField() and
|
||||
f = c.(NonUnionFieldContent).getField() and
|
||||
fieldHasApproxName(f, prefix)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -2093,8 +2093,8 @@ private Field getAFieldWithSize(Union u, int bytes) {
|
||||
|
||||
cached
|
||||
private newtype TContent =
|
||||
TFieldContent(Field f, int indirectionIndex) {
|
||||
// the indirection index for field content starts at 1 (because `TFieldContent` is thought of as
|
||||
TNonUnionContent(Field f, int indirectionIndex) {
|
||||
// the indirection index for field content starts at 1 (because `TNonUnionContent` is thought of as
|
||||
// the address of the field, `FieldAddress` in the IR).
|
||||
indirectionIndex = [1 .. SsaImpl::getMaxIndirectionsForType(f.getUnspecifiedType())] and
|
||||
// Reads and writes of union fields are tracked using `UnionContent`.
|
||||
@@ -2124,14 +2124,14 @@ private newtype TContent =
|
||||
*/
|
||||
class Content extends TContent {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
string toString() { none() } // overridden in subclasses
|
||||
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
}
|
||||
|
||||
/** Gets the indirection index of this `Content`. */
|
||||
abstract int getIndirectionIndex();
|
||||
int getIndirectionIndex() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
@@ -2142,7 +2142,7 @@ class Content extends TContent {
|
||||
* For example, a write to a field `f` implies that any content of
|
||||
* the form `*f` is also cleared.
|
||||
*/
|
||||
abstract predicate impliesClearOf(Content c);
|
||||
predicate impliesClearOf(Content c) { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2162,22 +2162,42 @@ private module ContentStars {
|
||||
|
||||
private import ContentStars
|
||||
|
||||
/** A reference through a non-union instance field. */
|
||||
private class TFieldContent = TNonUnionContent or TUnionContent;
|
||||
|
||||
/**
|
||||
* A `Content` that references a `Field`. This may be a field of a `struct`,
|
||||
* `class`, or `union`. In the case of a `union` there may be multiple fields
|
||||
* associated with the same `Content`.
|
||||
*/
|
||||
class FieldContent extends Content, TFieldContent {
|
||||
/** Gets a `Field` of this `Content`. */
|
||||
Field getAField() { none() }
|
||||
|
||||
/**
|
||||
* Gets the field associated with this `Content`, if a unique one exists.
|
||||
*/
|
||||
final Field getField() { result = unique( | | this.getAField()) }
|
||||
|
||||
override int getIndirectionIndex() { none() } // overridden in subclasses
|
||||
|
||||
override string toString() { none() } // overridden in subclasses
|
||||
|
||||
override predicate impliesClearOf(Content c) { none() } // overridden in subclasses
|
||||
}
|
||||
|
||||
/** A reference through a non-union instance field. */
|
||||
class NonUnionFieldContent extends FieldContent, TNonUnionContent {
|
||||
private Field f;
|
||||
private int indirectionIndex;
|
||||
|
||||
FieldContent() { this = TFieldContent(f, indirectionIndex) }
|
||||
NonUnionFieldContent() { this = TNonUnionContent(f, indirectionIndex) }
|
||||
|
||||
override string toString() { result = contentStars(this) + f.toString() }
|
||||
|
||||
Field getField() { result = f }
|
||||
override Field getAField() { result = f }
|
||||
|
||||
/** Gets the indirection index of this `FieldContent`. */
|
||||
pragma[inline]
|
||||
override int getIndirectionIndex() {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
|
||||
}
|
||||
override int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override predicate impliesClearOf(Content c) {
|
||||
exists(FieldContent fc |
|
||||
@@ -2191,7 +2211,7 @@ class FieldContent extends Content, TFieldContent {
|
||||
}
|
||||
|
||||
/** A reference through an instance field of a union. */
|
||||
class UnionContent extends Content, TUnionContent {
|
||||
class UnionContent extends FieldContent, TUnionContent {
|
||||
private Union u;
|
||||
private int indirectionIndex;
|
||||
private int bytes;
|
||||
@@ -2201,16 +2221,13 @@ class UnionContent extends Content, TUnionContent {
|
||||
override string toString() { result = contentStars(this) + u.toString() }
|
||||
|
||||
/** Gets a field of the underlying union of this `UnionContent`, if any. */
|
||||
Field getAField() { result = u.getAField() and getFieldSize(result) = bytes }
|
||||
override Field getAField() { result = u.getAField() and getFieldSize(result) = bytes }
|
||||
|
||||
/** Gets the underlying union of this `UnionContent`. */
|
||||
Union getUnion() { result = u }
|
||||
|
||||
/** Gets the indirection index of this `UnionContent`. */
|
||||
pragma[inline]
|
||||
override int getIndirectionIndex() {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
|
||||
}
|
||||
override int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override predicate impliesClearOf(Content c) {
|
||||
exists(UnionContent uc |
|
||||
@@ -2234,10 +2251,7 @@ class ElementContent extends Content, TElementContent {
|
||||
|
||||
ElementContent() { this = TElementContent(indirectionIndex) }
|
||||
|
||||
pragma[inline]
|
||||
override int getIndirectionIndex() {
|
||||
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
|
||||
}
|
||||
override int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override predicate impliesClearOf(Content c) { none() }
|
||||
|
||||
|
||||
@@ -47,6 +47,19 @@ compilation_args(
|
||||
string arg : string ref
|
||||
);
|
||||
|
||||
/**
|
||||
* The expanded arguments that were passed to the extractor for a
|
||||
* compiler invocation. This is similar to `compilation_args`, but
|
||||
* for a `@someFile` argument, it includes the arguments from that
|
||||
* file, rather than just taking the argument literally.
|
||||
*/
|
||||
#keyset[id, num]
|
||||
compilation_expanded_args(
|
||||
int id : @compilation ref,
|
||||
int num : int ref,
|
||||
string arg : string ref
|
||||
);
|
||||
|
||||
/**
|
||||
* Optionally, record the build mode for each compilation.
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Support expanded compilation argument lists
|
||||
compatibility: backwards
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.5.4
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.5.3
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
cpp/ql/src/change-notes/released/1.5.4.md
Normal file
3
cpp/ql/src/change-notes/released/1.5.4.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.5.4
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.5.3
|
||||
lastReleaseVersion: 1.5.4
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 1.5.4-dev
|
||||
version: 1.5.5-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -190,7 +190,7 @@ module ModelGeneratorCommonInput implements ModelGeneratorCommonInputSig<Cpp::Lo
|
||||
predicate isRelevantType(Type t) { any() }
|
||||
|
||||
Type getUnderlyingContentType(DataFlow::ContentSet c) {
|
||||
result = c.(DataFlow::FieldContent).getField().getUnspecifiedType() or
|
||||
result = c.(DataFlow::NonUnionFieldContent).getField().getUnspecifiedType() or
|
||||
result = c.(DataFlow::UnionContent).getUnion().getUnspecifiedType()
|
||||
}
|
||||
|
||||
@@ -340,12 +340,7 @@ private module SummaryModelGeneratorInput implements SummaryModelGeneratorInputS
|
||||
)
|
||||
}
|
||||
|
||||
predicate isField(DataFlow::ContentSet cs) {
|
||||
exists(DataFlow::Content c | cs.isSingleton(c) |
|
||||
c instanceof DataFlow::FieldContent or
|
||||
c instanceof DataFlow::UnionContent
|
||||
)
|
||||
}
|
||||
predicate isField(DataFlow::ContentSet cs) { cs.isSingleton(any(DataFlow::FieldContent fc)) }
|
||||
|
||||
predicate isCallback(DataFlow::ContentSet c) { none() }
|
||||
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
| sizeof.cpp:19:15:19:25 | sizeof(int) | 4 | SizeofTypeOperator.getTypeOperand() | file://:0:0:0:0 | int |
|
||||
| sizeof.cpp:20:15:20:26 | sizeof(char) | 1 | SizeofTypeOperator.getTypeOperand() | file://:0:0:0:0 | char |
|
||||
| sizeof.cpp:21:15:21:27 | sizeof(int *) | 8 | SizeofTypeOperator.getTypeOperand() | file://:0:0:0:0 | int * |
|
||||
| sizeof.cpp:22:15:22:29 | sizeof(MyClass) | 16 | SizeofTypeOperator.getTypeOperand() | sizeof.cpp:4:7:4:13 | MyClass |
|
||||
| sizeof.cpp:19:15:19:25 | sizeof(int) | 4 | SizeofOperator.getTypeOperand() | file://:0:0:0:0 | int |
|
||||
| sizeof.cpp:20:15:20:26 | sizeof(char) | 1 | SizeofOperator.getTypeOperand() | file://:0:0:0:0 | char |
|
||||
| sizeof.cpp:21:15:21:27 | sizeof(int *) | 8 | SizeofOperator.getTypeOperand() | file://:0:0:0:0 | int * |
|
||||
| sizeof.cpp:22:15:22:29 | sizeof(MyClass) | 16 | SizeofOperator.getTypeOperand() | sizeof.cpp:4:7:4:13 | MyClass |
|
||||
| sizeof.cpp:23:15:23:23 | sizeof(<expr>) | 4 | SizeofExprOperator.getExprOperand() | sizeof.cpp:23:22:23:22 | i |
|
||||
| sizeof.cpp:23:15:23:23 | sizeof(<expr>) | 4 | SizeofOperator.getTypeOperand() | file://:0:0:0:0 | int |
|
||||
| sizeof.cpp:24:15:24:23 | sizeof(<expr>) | 1 | SizeofExprOperator.getExprOperand() | sizeof.cpp:24:22:24:22 | c |
|
||||
| sizeof.cpp:24:15:24:23 | sizeof(<expr>) | 1 | SizeofOperator.getTypeOperand() | file://:0:0:0:0 | char |
|
||||
| sizeof.cpp:25:15:25:25 | sizeof(<expr>) | 8 | SizeofExprOperator.getExprOperand() | sizeof.cpp:25:22:25:24 | ptr |
|
||||
| sizeof.cpp:25:15:25:25 | sizeof(<expr>) | 8 | SizeofOperator.getTypeOperand() | file://:0:0:0:0 | int * |
|
||||
| sizeof.cpp:26:15:26:24 | sizeof(<expr>) | 16 | SizeofExprOperator.getExprOperand() | sizeof.cpp:26:22:26:23 | mc |
|
||||
| sizeof.cpp:26:15:26:24 | sizeof(<expr>) | 16 | SizeofOperator.getTypeOperand() | sizeof.cpp:4:7:4:13 | MyClass |
|
||||
| sizeof.cpp:27:15:27:25 | sizeof(<expr>) | 40 | SizeofExprOperator.getExprOperand() | sizeof.cpp:27:22:27:24 | arr |
|
||||
| sizeof.cpp:27:15:27:25 | sizeof(<expr>) | 40 | SizeofOperator.getTypeOperand() | file://:0:0:0:0 | int[10] |
|
||||
| sizeof.cpp:28:16:28:29 | sizeof(<expr>) | 4 | SizeofExprOperator.getExprOperand() | sizeof.cpp:28:23:28:28 | access to array |
|
||||
| sizeof.cpp:28:16:28:29 | sizeof(<expr>) | 4 | SizeofOperator.getTypeOperand() | file://:0:0:0:0 | int |
|
||||
|
||||
@@ -2,8 +2,8 @@ import cpp
|
||||
|
||||
from SizeofOperator sto, string elemDesc, Element e
|
||||
where
|
||||
elemDesc = "SizeofTypeOperator.getTypeOperand()" and
|
||||
e = sto.(SizeofTypeOperator).getTypeOperand()
|
||||
elemDesc = "SizeofOperator.getTypeOperand()" and
|
||||
e = sto.getTypeOperand()
|
||||
or
|
||||
elemDesc = "SizeofExprOperator.getExprOperand()" and
|
||||
e = sto.(SizeofExprOperator).getExprOperand()
|
||||
|
||||
@@ -170,7 +170,8 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
fileLogger.LogError($" Unhandled exception: {ex}");
|
||||
}
|
||||
|
||||
logger.Log(Severity.Info, $"Extraction completed in {overallStopwatch.Elapsed}");
|
||||
logger.Log(Severity.Info, $"Extraction completed in {analyzerStopwatch.Elapsed}");
|
||||
logger.Log(Severity.Info, $"Total time: {overallStopwatch.Elapsed}");
|
||||
|
||||
return ExitCode.Ok;
|
||||
}
|
||||
|
||||
@@ -360,5 +360,22 @@ namespace Semmle.Extraction.CSharp
|
||||
return versionString.InformationalVersion;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> errorsToIgnore = new HashSet<string>
|
||||
{
|
||||
"CS7027", // Code signing failure
|
||||
"CS1589", // XML referencing not supported
|
||||
"CS1569" // Error writing XML documentation
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the diagnostics from the compilation, filtering out those that should be ignored.
|
||||
/// </summary>
|
||||
protected List<Diagnostic> GetFilteredDiagnostics() =>
|
||||
compilation is not null
|
||||
? compilation.GetDiagnostics()
|
||||
.Where(e => e.Severity >= DiagnosticSeverity.Error && !errorsToIgnore.Contains(e.Id))
|
||||
.ToList()
|
||||
: [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,14 @@ namespace Semmle.Extraction.CSharp
|
||||
{
|
||||
}
|
||||
|
||||
private void LogDiagnostics()
|
||||
{
|
||||
foreach (var error in GetFilteredDiagnostics())
|
||||
{
|
||||
Logger.LogDebug($" Compilation error: {error}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize(string outputPath, IEnumerable<(string, string)> compilationInfos, CSharpCompilation compilationIn, CommonOptions options)
|
||||
{
|
||||
compilation = compilationIn;
|
||||
@@ -20,6 +28,7 @@ namespace Semmle.Extraction.CSharp
|
||||
this.options = options;
|
||||
LogExtractorInfo();
|
||||
SetReferencePaths();
|
||||
LogDiagnostics();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,11 +136,7 @@ namespace Semmle.Extraction.CSharp
|
||||
|
||||
private int LogDiagnostics()
|
||||
{
|
||||
var filteredDiagnostics = compilation!
|
||||
.GetDiagnostics()
|
||||
.Where(e => e.Severity >= DiagnosticSeverity.Error && !errorsToIgnore.Contains(e.Id))
|
||||
.ToList();
|
||||
|
||||
var filteredDiagnostics = GetFilteredDiagnostics();
|
||||
foreach (var error in filteredDiagnostics)
|
||||
{
|
||||
Logger.LogError($" Compilation error: {error}");
|
||||
@@ -148,7 +144,7 @@ namespace Semmle.Extraction.CSharp
|
||||
|
||||
if (filteredDiagnostics.Count != 0)
|
||||
{
|
||||
foreach (var reference in compilation.References)
|
||||
foreach (var reference in compilation!.References)
|
||||
{
|
||||
Logger.LogInfo($" Resolved reference {reference.Display}");
|
||||
}
|
||||
@@ -156,12 +152,5 @@ namespace Semmle.Extraction.CSharp
|
||||
|
||||
return filteredDiagnostics.Count;
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> errorsToIgnore = new HashSet<string>
|
||||
{
|
||||
"CS7027", // Code signing failure
|
||||
"CS1589", // XML referencing not supported
|
||||
"CS1569" // Error writing XML documentation
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.7.52
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.7.51
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.7.52
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.7.51
|
||||
lastReleaseVersion: 1.7.52
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.7.52-dev
|
||||
version: 1.7.53-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.7.52
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.7.51
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.7.52
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.7.51
|
||||
lastReleaseVersion: 1.7.52
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.7.52-dev
|
||||
version: 1.7.53-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
## 5.4.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* `ControlFlowElement.controlsBlock` has been deprecated in favor of the Guards library.
|
||||
|
||||
### New Features
|
||||
|
||||
* Initial support for incremental C# databases via `codeql database create --overlay-base`/`--overlay-changes`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Updated *roslyn* and *binlog* dependencies in the extractor, which may improve database and analysis quality.
|
||||
|
||||
## 5.3.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* `ControlFlowElement.controlsBlock` has been deprecated in favor of the Guards library.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
|
||||
* Initial support for incremental C# databases via `codeql database create --overlay-base`/`--overlay-changes`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Updated *roslyn* and *binlog* dependencies in the extractor, which may improve database and analysis quality.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Compilation errors are now included in the debug log when using build-mode none.
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* The representation of the C# control-flow graph has been significantly changed. This has minor effects on a wide range of queries including both minor improvements and minor regressions, for example, improved precision has been observed for `cs/inefficient-containskey` and `cs/stringbuilder-creation-in-loop`. Two queries stand out as being significantly affected with great improvements: `cs/dereferenced-value-may-be-null` has been completely rewritten which removes a very significant number of false positives. Furthermore, `cs/constant-condition` has been updated to report many new results - these new results are primarily expected to be true positives, but a few new false positives are expected as well. As part of these changes, `cs/dereferenced-value-may-be-null` has been changed from a `path-problem` query to a `problem` query, so paths are no longer reported for this query.
|
||||
* The representation of the C# control-flow graph has been significantly changed. This has minor effects on a wide range of queries including both minor improvements and minor regressions. For example, improved precision has been observed for `cs/inefficient-containskey` and `cs/stringbuilder-creation-in-loop`. Two queries stand out as being significantly affected with great improvements: `cs/dereferenced-value-may-be-null` has been completely rewritten which removes a very significant number of false positives. Furthermore, `cs/constant-condition` has been updated to report many new results - these new results are primarily expected to be true positives, but a few new false positives are expected as well. As part of these changes, `cs/dereferenced-value-may-be-null` has been changed from a `path-problem` query to a `problem` query, so paths are no longer reported for this query.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
|
||||
13
csharp/ql/lib/change-notes/released/5.4.0.md
Normal file
13
csharp/ql/lib/change-notes/released/5.4.0.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## 5.4.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* `ControlFlowElement.controlsBlock` has been deprecated in favor of the Guards library.
|
||||
|
||||
### New Features
|
||||
|
||||
* Initial support for incremental C# databases via `codeql database create --overlay-base`/`--overlay-changes`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Updated *roslyn* and *binlog* dependencies in the extractor, which may improve database and analysis quality.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 5.3.0
|
||||
lastReleaseVersion: 5.4.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 5.3.1-dev
|
||||
version: 5.4.1-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
## 1.5.0
|
||||
|
||||
### New Queries
|
||||
|
||||
* The `cs/web/cookie-secure-not-set` and `cs/web/cookie-httponly-not-set` queries have been promoted from experimental to the main query pack.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* An improvement to the Guards library for recognizing disjunctions means improved precision for `cs/constant-condition`, `cs/inefficient-containskey`, and `cs/dereferenced-value-may-be-null`. The two former can have additional findings, and the latter will have fewer false positives.
|
||||
|
||||
## 1.4.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
The `cs/web/cookie-secure-not-set` and `cs/web/cookie-httponly-not-set` queries have been promoted from experimental to the main query pack.
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* the `cs/web/missing-x-frame-options` query now correctly handles configuration nested in root `<location>` elements.
|
||||
* The `cs/web/missing-x-frame-options` query now correctly handles configuration nested in root `<location>` elements.
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 1.5.0
|
||||
|
||||
### New Queries
|
||||
|
||||
* The `cs/web/cookie-secure-not-set` and `cs/web/cookie-httponly-not-set` queries have been promoted from experimental to the main query pack.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* An improvement to the Guards library for recognizing disjunctions means improved precision for `cs/constant-condition`, `cs/inefficient-containskey`, and `cs/dereferenced-value-may-be-null`. The two former can have additional findings, and the latter will have fewer false positives.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.4.3
|
||||
lastReleaseVersion: 1.5.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 1.4.4-dev
|
||||
version: 1.5.1-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
.. _codeql-cli-2.23.5:
|
||||
|
||||
==========================
|
||||
CodeQL 2.23.5 (2025-11-13)
|
||||
==========================
|
||||
|
||||
.. contents:: Contents
|
||||
:depth: 2
|
||||
:local:
|
||||
:backlinks: none
|
||||
|
||||
This is an overview of changes in the CodeQL CLI and relevant CodeQL query and library packs. For additional updates on changes to the CodeQL code scanning experience, check out the `code scanning section on the GitHub blog <https://github.blog/tag/code-scanning/>`__, `relevant GitHub Changelog updates <https://github.blog/changelog/label/application-security/>`__, `changes in the CodeQL extension for Visual Studio Code <https://marketplace.visualstudio.com/items/GitHub.vscode-codeql/changelog>`__, and the `CodeQL Action changelog <https://github.com/github/codeql-action/blob/main/CHANGELOG.md>`__.
|
||||
|
||||
Security Coverage
|
||||
-----------------
|
||||
|
||||
CodeQL 2.23.5 runs a total of 483 security queries when configured with the Default suite (covering 166 CWE). The Extended suite enables an additional 135 queries (covering 35 more CWE). 3 security queries have been added with this release.
|
||||
|
||||
CodeQL CLI
|
||||
----------
|
||||
|
||||
Breaking Changes
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
* In order to make a :code:`@kind path-problem` query diff-informed, the :code:`getASelectedSourceLocation` and :code:`getASelectedSinkLocation` predicates in the dataflow configuration now need to be overridden to always return the location of the source/sink *in addition to* any other locations that are selected by the query. See the `QLdoc <https://github.com/github/codeql/blob/d122534398c5eb9182a23a9ad65caa5937d627b5/shared/dataflow/codeql/dataflow/DataFlow.qll#L474>`__ for more details.
|
||||
|
||||
Query Packs
|
||||
-----------
|
||||
|
||||
Minor Analysis Improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
C#
|
||||
""
|
||||
|
||||
* the :code:`cs/web/missing-x-frame-options` query now correctly handles configuration nested in root :code:`<location>` elements.
|
||||
|
||||
Java/Kotlin
|
||||
"""""""""""
|
||||
|
||||
* Calls to :code:`String.matches` are now treated as sanitizers for the :code:`java/ssrf` query.
|
||||
|
||||
Python
|
||||
""""""
|
||||
|
||||
* The :code:`py/insecure-cookie` query has been split into multiple queries; with :code:`py/insecure-cookie` checking for cases in which :code:`Secure` flag is not set, :code:`py/client-exposed-cookie` checking for cases in which the :code:`HttpOnly` flag is not set, and the :code:`py/samesite-none` query checking for cases in which the :code:`SameSite` attribute is set to :code:`None`. These queries also now only alert for cases in which the cookie is detected to contain sensitive data.
|
||||
|
||||
Rust
|
||||
""""
|
||||
|
||||
* The "Low Rust analysis quality" query (:code:`rust/diagnostic/database-quality`), used by the tool status page, has been extended with a measure of successful type inference.
|
||||
|
||||
New Queries
|
||||
~~~~~~~~~~~
|
||||
|
||||
Java/Kotlin
|
||||
"""""""""""
|
||||
|
||||
* The :code:`java/sensitive-cookie-not-httponly` query has been promoted from experimental to the main query pack.
|
||||
* Added a new query, :code:`java/escaping`, to detect values escaping from classes marked as :code:`@ThreadSafe`.
|
||||
* Added a new query, :code:`java/not-threadsafe`, to detect data races in classes marked as :code:`@ThreadSafe`.
|
||||
* Added a new query, :code:`java/safe-publication`, to detect unsafe publication in classes marked as :code:`@ThreadSafe`.
|
||||
|
||||
Language Libraries
|
||||
------------------
|
||||
|
||||
Breaking Changes
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Swift
|
||||
"""""
|
||||
|
||||
* The :code:`OpenedArchetypeType` class has been renamed as :code:`ExistentialArchetypeType`.
|
||||
* The :code:`OtherAvailabilitySpec` class has been removed. Use :code:`AvailabilitySpec::isWildcard` instead.
|
||||
* The :code:`PlatformVersionAvailabilitySpec` has been removed. Use :code:`AvailabilitySpec::getPlatform` and :code:`AvailabilitySpec::getVersion` instead.
|
||||
|
||||
Major Analysis Improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
C#
|
||||
""
|
||||
|
||||
* The representation of the C# control-flow graph has been significantly changed. This has minor effects on a wide range of queries including both minor improvements and minor regressions, for example, improved precision has been observed for :code:`cs/inefficient-containskey` and :code:`cs/stringbuilder-creation-in-loop`. Two queries stand out as being significantly affected with great improvements: :code:`cs/dereferenced-value-may-be-null` has been completely rewritten which removes a very significant number of false positives. Furthermore, :code:`cs/constant-condition` has been updated to report many new results - these new results are primarily expected to be true positives, but a few new false positives are expected as well. As part of these changes, :code:`cs/dereferenced-value-may-be-null` has been changed from a :code:`path-problem` query to a :code:`problem` query, so paths are no longer reported for this query.
|
||||
|
||||
Swift
|
||||
"""""
|
||||
|
||||
* Upgraded to allow analysis of Swift 6.2.
|
||||
* Support for experimental Embedded Swift has been dropped.
|
||||
|
||||
Rust
|
||||
""""
|
||||
|
||||
* Resolution of calls to functions has been improved in a number of ways, to make it more aligned with the behavior of the Rust compiler. This may impact queries that rely on call resolution, such as data flow queries.
|
||||
* Added basic models for the :code:`actix-web` web framework.
|
||||
|
||||
Minor Analysis Improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
C#
|
||||
""
|
||||
|
||||
* Added tracer support for macOS and Linux when the .NET CLI (:code:`dotnet`) directly invokes the C# compiler (:code:`csc`). This enhancement provides basic tracing and extraction capabilities for .NET 10 RC2 on these platforms.
|
||||
* The extraction of location information for source code entities has been updated to use star IDs (:code:`*` IDs). This change should be transparent to end-users but may improve extraction performance in some cases by reducing TRAP file size and eliminating overhead from location de-duplication.
|
||||
|
||||
Rust
|
||||
""""
|
||||
|
||||
* Added :code:`ExtractedFile::hasSemantics` and :code:`ExtractedFile::isSkippedByCompilation` predicates.
|
||||
* Generalized some existing models to improve data flow.
|
||||
* Added models for the :code:`mysql` and :code:`mysql_async` libraries.
|
||||
|
||||
Deprecated APIs
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
C#
|
||||
""
|
||||
|
||||
* The class :code:`AbstractValue` in the :code:`Guards` library has been deprecated and replaced with the class :code:`GuardValue`.
|
||||
|
||||
New Features
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Python
|
||||
""""""
|
||||
|
||||
* Initial support for incremental Python databases via :code:`codeql database create --overlay-base`\ /\ :code:`--overlay-changes`.
|
||||
|
||||
Swift
|
||||
"""""
|
||||
|
||||
* Added AST nodes :code:`UsingDecl`, :code:`UnsafeExpr`, and :code:`InlineArrayType` that correspond to new nodes in Swift 6.2.
|
||||
* Added new predicates :code:`isDistributedGet`, :code:`isRead2`, :code:`isModify2`, and :code:`isInit` to the :code:`Accessor` class that correspond to new accessors in Swift 6.2.
|
||||
* Added a new predicate :code:`isApply` to the :code:`KeyPathComponent` class that corresponds to method and initializer key path components in Swift 6.2.
|
||||
@@ -11,6 +11,7 @@ A list of queries for each suite and language `is available here <https://docs.g
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
codeql-cli-2.23.5
|
||||
codeql-cli-2.23.3
|
||||
codeql-cli-2.23.2
|
||||
codeql-cli-2.23.1
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
- `CodeQL queries for Rust <https://github.com/github/codeql/tree/main/rust/ql/src>`__
|
||||
- `Example queries for Rust <https://github.com/github/codeql/tree/main/rust/ql/examples>`__
|
||||
- `CodeQL library reference for Rust <https://codeql.github.com/codeql-standard-libraries/rust/>`__
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.0.35
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.0.34
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.0.35
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.0.34
|
||||
lastReleaseVersion: 1.0.35
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql-go-consistency-queries
|
||||
version: 1.0.35-dev
|
||||
version: 1.0.36-dev
|
||||
groups:
|
||||
- go
|
||||
- queries
|
||||
|
||||
@@ -18,7 +18,9 @@ ql/go/ql/src/Security/CWE-295/DisabledCertificateCheck.ql
|
||||
ql/go/ql/src/Security/CWE-312/CleartextLogging.ql
|
||||
ql/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.ql
|
||||
ql/go/ql/src/Security/CWE-326/InsufficientKeySize.ql
|
||||
ql/go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql
|
||||
ql/go/ql/src/Security/CWE-327/InsecureTLS.ql
|
||||
ql/go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql
|
||||
ql/go/ql/src/Security/CWE-338/InsecureRandomness.ql
|
||||
ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql
|
||||
ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql
|
||||
|
||||
@@ -41,7 +41,9 @@ ql/go/ql/src/Security/CWE-295/DisabledCertificateCheck.ql
|
||||
ql/go/ql/src/Security/CWE-312/CleartextLogging.ql
|
||||
ql/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.ql
|
||||
ql/go/ql/src/Security/CWE-326/InsufficientKeySize.ql
|
||||
ql/go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql
|
||||
ql/go/ql/src/Security/CWE-327/InsecureTLS.ql
|
||||
ql/go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql
|
||||
ql/go/ql/src/Security/CWE-338/InsecureRandomness.ql
|
||||
ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql
|
||||
ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql
|
||||
|
||||
@@ -19,7 +19,9 @@ ql/go/ql/src/Security/CWE-295/DisabledCertificateCheck.ql
|
||||
ql/go/ql/src/Security/CWE-312/CleartextLogging.ql
|
||||
ql/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.ql
|
||||
ql/go/ql/src/Security/CWE-326/InsufficientKeySize.ql
|
||||
ql/go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql
|
||||
ql/go/ql/src/Security/CWE-327/InsecureTLS.ql
|
||||
ql/go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql
|
||||
ql/go/ql/src/Security/CWE-338/InsecureRandomness.ql
|
||||
ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql
|
||||
ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql
|
||||
|
||||
@@ -14,7 +14,6 @@ ql/go/ql/src/experimental/CWE-203/Timing.ql
|
||||
ql/go/ql/src/experimental/CWE-285/PamAuthBypass.ql
|
||||
ql/go/ql/src/experimental/CWE-287/ImproperLdapAuth.ql
|
||||
ql/go/ql/src/experimental/CWE-321-V2/HardCodedKeys.ql
|
||||
ql/go/ql/src/experimental/CWE-327/WeakCryptoAlgorithm.ql
|
||||
ql/go/ql/src/experimental/CWE-369/DivideByZero.ql
|
||||
ql/go/ql/src/experimental/CWE-400/DatabaseCallInLoop.ql
|
||||
ql/go/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
## 5.0.2
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Some fixes relating to use of path transformers when extracting a database:
|
||||
* Fixed a problem where the path transformer would be ignored when extracting older codebases that predate the use of Go modules.
|
||||
* The environment variable `CODEQL_PATH_TRANSFORMER` is now recognized, in addition to `SEMMLE_PATH_TRANSFORMER`.
|
||||
* Fixed some cases where the extractor emitted paths without applying the path transformer.
|
||||
|
||||
## 5.0.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
## 5.0.2
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Some fixes relating to use of path transformers when extracting a database:
|
||||
* Fixed a problem where the path transformer would be ignored when extracting older codebases that predate the use of Go modules.
|
||||
* The environment variable `CODEQL_PATH_TRANSFORMER` is now recognized, in addition to `SEMMLE_PATH_TRANSFORMER`.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 5.0.1
|
||||
lastReleaseVersion: 5.0.2
|
||||
|
||||
@@ -33,6 +33,7 @@ import semmle.go.frameworks.AwsLambda
|
||||
import semmle.go.frameworks.Beego
|
||||
import semmle.go.frameworks.BeegoOrm
|
||||
import semmle.go.frameworks.Bun
|
||||
import semmle.go.frameworks.CryptoLibraries
|
||||
import semmle.go.frameworks.RsCors
|
||||
import semmle.go.frameworks.Couchbase
|
||||
import semmle.go.frameworks.Echo
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
name: codeql/go-all
|
||||
version: 5.0.2-dev
|
||||
version: 5.0.3-dev
|
||||
groups: go
|
||||
dbscheme: go.dbscheme
|
||||
extractor: go
|
||||
library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/concepts: ${workspace}
|
||||
codeql/dataflow: ${workspace}
|
||||
codeql/mad: ${workspace}
|
||||
codeql/threat-models: ${workspace}
|
||||
|
||||
@@ -8,6 +8,10 @@ import go
|
||||
import semmle.go.dataflow.FunctionInputsAndOutputs
|
||||
import semmle.go.concepts.HTTP
|
||||
import semmle.go.concepts.GeneratedFile
|
||||
private import codeql.concepts.ConceptsShared
|
||||
private import semmle.go.dataflow.internal.DataFlowImplSpecific
|
||||
|
||||
private module ConceptsShared = ConceptsMake<Location, GoDataFlow>;
|
||||
|
||||
/**
|
||||
* A data-flow node that executes an operating system command,
|
||||
@@ -505,3 +509,98 @@ module UnmarshalingFunction {
|
||||
abstract string getFormat();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for cryptographic things.
|
||||
*/
|
||||
module Cryptography {
|
||||
private import ConceptsShared::Cryptography as SC
|
||||
|
||||
/**
|
||||
* A data-flow node that is an application of a cryptographic algorithm. For example,
|
||||
* encryption, decryption, signature-validation.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `CryptographicOperation::Range` instead.
|
||||
*/
|
||||
class CryptographicOperation extends SC::CryptographicOperation { }
|
||||
|
||||
class EncryptionAlgorithm = SC::EncryptionAlgorithm;
|
||||
|
||||
class HashingAlgorithm = SC::HashingAlgorithm;
|
||||
|
||||
class PasswordHashingAlgorithm = SC::PasswordHashingAlgorithm;
|
||||
|
||||
module CryptographicOperation = SC::CryptographicOperation;
|
||||
|
||||
class BlockMode = SC::BlockMode;
|
||||
|
||||
class CryptographicAlgorithm = SC::CryptographicAlgorithm;
|
||||
|
||||
/** A data flow node that initializes a hash algorithm. */
|
||||
abstract class HashAlgorithmInit extends DataFlow::Node {
|
||||
/** Gets the hash algorithm being initialized. */
|
||||
abstract HashingAlgorithm getAlgorithm();
|
||||
}
|
||||
|
||||
/** A data flow node that is an application of a hash algorithm. */
|
||||
abstract class HashOperation extends CryptographicOperation::Range {
|
||||
override BlockMode getBlockMode() { none() }
|
||||
}
|
||||
|
||||
/** A data flow node that initializes an encryption algorithm. */
|
||||
abstract class EncryptionAlgorithmInit extends DataFlow::Node {
|
||||
/** Gets the encryption algorithm being initialized. */
|
||||
abstract EncryptionAlgorithm getAlgorithm();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that initializes a block cipher mode of operation, and
|
||||
* may also propagate taint for encryption algorithms.
|
||||
*/
|
||||
abstract class BlockModeInit extends DataFlow::CallNode {
|
||||
/** Gets the block cipher mode of operation being initialized. */
|
||||
abstract BlockMode getMode();
|
||||
|
||||
/** Gets a step propagating the encryption algorithm through this call. */
|
||||
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that is an application of an encryption algorithm, where
|
||||
* the encryption algorithm and the block cipher mode of operation (if there
|
||||
* is one) have been initialized separately.
|
||||
*/
|
||||
abstract class EncryptionOperation extends CryptographicOperation::Range {
|
||||
DataFlow::Node encryptionFlowTarget;
|
||||
DataFlow::Node inputNode;
|
||||
|
||||
override DataFlow::Node getInitialization() {
|
||||
EncryptionFlow::flow(result, encryptionFlowTarget)
|
||||
}
|
||||
|
||||
override EncryptionAlgorithm getAlgorithm() {
|
||||
result = this.getInitialization().(EncryptionAlgorithmInit).getAlgorithm()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnInput() { result = inputNode }
|
||||
|
||||
override BlockMode getBlockMode() {
|
||||
result = this.getInitialization().(BlockModeInit).getMode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `EncryptionOperation` which is a method call where the encryption
|
||||
* algorithm and block cipher mode of operation (if there is one) flow to the
|
||||
* receiver and the input is an argument.
|
||||
*/
|
||||
abstract class EncryptionMethodCall extends EncryptionOperation instanceof DataFlow::CallNode {
|
||||
int inputArg;
|
||||
|
||||
EncryptionMethodCall() {
|
||||
encryptionFlowTarget = super.getReceiver() and
|
||||
inputNode = super.getArgument(inputArg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
484
go/ql/lib/semmle/go/frameworks/CryptoLibraries.qll
Normal file
484
go/ql/lib/semmle/go/frameworks/CryptoLibraries.qll
Normal file
@@ -0,0 +1,484 @@
|
||||
/**
|
||||
* Provides classes for modeling cryptographic libraries.
|
||||
*/
|
||||
|
||||
import go
|
||||
import semmle.go.Concepts::Cryptography
|
||||
private import codeql.concepts.internal.CryptoAlgorithmNames
|
||||
|
||||
/**
|
||||
* A data flow call node that is an application of a hash operation where the
|
||||
* hash algorithm is defined in any earlier initialization node, and the input
|
||||
* is the first argument of the call.
|
||||
*/
|
||||
abstract class DirectHashOperation extends HashOperation instanceof DataFlow::CallNode {
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
|
||||
}
|
||||
|
||||
private module HashConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof HashAlgorithmInit }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { any() }
|
||||
}
|
||||
|
||||
/** Tracks the flow of hash algorithms. */
|
||||
module HashFlow = DataFlow::Global<HashConfig>;
|
||||
|
||||
/**
|
||||
* A data flow node that initializes a block mode and propagates the encryption
|
||||
* algorithm from the first argument to the receiver.
|
||||
*/
|
||||
abstract class StdLibNewEncrypter extends BlockModeInit {
|
||||
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
node1 = this.getArgument(0) and
|
||||
node2 = this.getResult(0)
|
||||
}
|
||||
}
|
||||
|
||||
private module EncryptionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source instanceof EncryptionAlgorithmInit or
|
||||
source instanceof BlockModeInit
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { any() }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
any(BlockModeInit nbcm).step(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks algorithms and block cipher modes of operation used for encryption.
|
||||
*/
|
||||
module EncryptionFlow = DataFlow::Global<EncryptionConfig>;
|
||||
|
||||
private module Crypto {
|
||||
private module Aes {
|
||||
private class NewCipher extends EncryptionAlgorithmInit {
|
||||
NewCipher() {
|
||||
exists(Function f | this = f.getACall().getResult(0) |
|
||||
f.hasQualifiedName("crypto/aes", "NewCipher")
|
||||
)
|
||||
}
|
||||
|
||||
override EncryptionAlgorithm getAlgorithm() { result.matchesName("AES") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Des {
|
||||
private class NewCipher extends EncryptionAlgorithmInit {
|
||||
NewCipher() {
|
||||
exists(Function f | this = f.getACall().getResult(0) |
|
||||
f.hasQualifiedName("crypto/des", "NewCipher")
|
||||
)
|
||||
}
|
||||
|
||||
override EncryptionAlgorithm getAlgorithm() { result.matchesName("DES") }
|
||||
}
|
||||
|
||||
private class NewTripleDESCipher extends EncryptionAlgorithmInit {
|
||||
NewTripleDESCipher() {
|
||||
exists(Function f | this = f.getACall().getResult(0) |
|
||||
f.hasQualifiedName("crypto/des", "NewTripleDESCipher")
|
||||
)
|
||||
}
|
||||
|
||||
override EncryptionAlgorithm getAlgorithm() { result.matchesName("TRIPLEDES") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Md5 {
|
||||
private class Sum extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum() { this.getTarget().hasQualifiedName("crypto/md5", "Sum") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("MD5") }
|
||||
}
|
||||
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("crypto/md5", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("MD5") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Rc4 {
|
||||
private class CipherXorKeyStream extends CryptographicOperation::Range instanceof DataFlow::CallNode
|
||||
{
|
||||
CipherXorKeyStream() {
|
||||
this.(DataFlow::MethodCallNode)
|
||||
.getTarget()
|
||||
.hasQualifiedName("crypto/rc4", "Cipher", "XORKeyStream")
|
||||
}
|
||||
|
||||
override DataFlow::Node getInitialization() { result = this }
|
||||
|
||||
override EncryptionAlgorithm getAlgorithm() { result.matchesName("RC4") }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = super.getArgument(1) }
|
||||
|
||||
override BlockMode getBlockMode() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cryptographic operations from the `crypto/sha1` package.
|
||||
*/
|
||||
private module Sha1 {
|
||||
private class Sum extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum() { this.getTarget().hasQualifiedName("crypto/sha1", "Sum") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA1") }
|
||||
}
|
||||
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("crypto/sha1", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA1") }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cryptographic operations from the `crypto/sha256` package.
|
||||
*/
|
||||
private module Sha256 {
|
||||
private class Sum256 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum256() { this.getTarget().hasQualifiedName("crypto/sha256", "Sum256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA256") }
|
||||
}
|
||||
|
||||
private class Sum224 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum224() { this.getTarget().hasQualifiedName("crypto/sha256", "Sum224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA224") }
|
||||
}
|
||||
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("crypto/sha256", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA256") }
|
||||
}
|
||||
|
||||
private class New224 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New224() { this.getTarget().hasQualifiedName("crypto/sha256", "New224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA224") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Sha3 {
|
||||
private class Sum224 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum224() { this.getTarget().hasQualifiedName("crypto/sha3", "Sum224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3224") }
|
||||
}
|
||||
|
||||
private class Sum256 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum256() { this.getTarget().hasQualifiedName("crypto/sha3", "Sum256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3256") }
|
||||
}
|
||||
|
||||
private class Sum384 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum384() { this.getTarget().hasQualifiedName("crypto/sha3", "Sum384") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3384") }
|
||||
}
|
||||
|
||||
private class Sum512 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum512() { this.getTarget().hasQualifiedName("crypto/sha3", "Sum512") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3512") }
|
||||
}
|
||||
|
||||
private class SumShake128 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
SumShake128() { this.getTarget().hasQualifiedName("crypto/sha3", "SumSHAKE128") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHAKE128") }
|
||||
}
|
||||
|
||||
private class SumShake256 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
SumShake256() { this.getTarget().hasQualifiedName("crypto/sha3", "SumSHAKE256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHAKE256") }
|
||||
}
|
||||
|
||||
private class New224 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New224() { this.getTarget().hasQualifiedName("crypto/sha3", "New224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3224") }
|
||||
}
|
||||
|
||||
private class New256 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New256() { this.getTarget().hasQualifiedName("crypto/sha3", "New256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3256") }
|
||||
}
|
||||
|
||||
private class New384 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New384() { this.getTarget().hasQualifiedName("crypto/sha3", "New384") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3384") }
|
||||
}
|
||||
|
||||
private class New512 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New512() { this.getTarget().hasQualifiedName("crypto/sha3", "New512") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA3512") }
|
||||
}
|
||||
|
||||
private class NewShake128 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
NewShake128() {
|
||||
this.getTarget().hasQualifiedName("crypto/sha3", ["NewCSHAKE128", "NewSHAKE128"])
|
||||
}
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHAKE128") }
|
||||
}
|
||||
|
||||
private class NewShake256 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
NewShake256() {
|
||||
this.getTarget().hasQualifiedName("crypto/sha3", ["NewCSHAKE256", "NewSHAKE256"])
|
||||
}
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHAKE256") }
|
||||
}
|
||||
|
||||
private class ShakeWrite extends HashOperation instanceof DataFlow::MethodCallNode {
|
||||
ShakeWrite() { this.getTarget().hasQualifiedName("crypto/sha3", "SHAKE", "Write") }
|
||||
|
||||
override HashAlgorithmInit getInitialization() { HashFlow::flow(result, super.getReceiver()) }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result = this.getInitialization().getAlgorithm() }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
|
||||
}
|
||||
}
|
||||
|
||||
private module Sha512 {
|
||||
private class Sum384 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum384() { this.getTarget().hasQualifiedName("crypto/sha512", "Sum384") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA384") }
|
||||
}
|
||||
|
||||
private class Sum512 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum512() { this.getTarget().hasQualifiedName("crypto/sha512", "Sum512") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512") }
|
||||
}
|
||||
|
||||
private class Sum512_224 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum512_224() { this.getTarget().hasQualifiedName("crypto/sha512", "Sum512_224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512224") }
|
||||
}
|
||||
|
||||
private class Sum512_256 extends DirectHashOperation instanceof DataFlow::CallNode {
|
||||
Sum512_256() { this.getTarget().hasQualifiedName("crypto/sha512", "Sum512_256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512256") }
|
||||
}
|
||||
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("crypto/sha512", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512") }
|
||||
}
|
||||
|
||||
private class New384 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New384() { this.getTarget().hasQualifiedName("crypto/sha512", "New384") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA384") }
|
||||
}
|
||||
|
||||
private class New512_224 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New512_224() { this.getTarget().hasQualifiedName("crypto/sha512", "New512_224") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512224") }
|
||||
}
|
||||
|
||||
private class New512_256 extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New512_256() { this.getTarget().hasQualifiedName("crypto/sha512", "New512_256") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("SHA512256") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Cipher {
|
||||
private class NewCbcEncrypter extends StdLibNewEncrypter {
|
||||
NewCbcEncrypter() { this.getTarget().hasQualifiedName("crypto/cipher", "NewCBCEncrypter") }
|
||||
|
||||
override BlockMode getMode() { result = "CBC" }
|
||||
}
|
||||
|
||||
private class NewCfbEncrypter extends StdLibNewEncrypter {
|
||||
NewCfbEncrypter() { this.getTarget().hasQualifiedName("crypto/cipher", "NewCFBEncrypter") }
|
||||
|
||||
override BlockMode getMode() { result = "CFB" }
|
||||
}
|
||||
|
||||
private class NewCtr extends StdLibNewEncrypter {
|
||||
NewCtr() { this.getTarget().hasQualifiedName("crypto/cipher", "NewCTR") }
|
||||
|
||||
override BlockMode getMode() { result = "CTR" }
|
||||
}
|
||||
|
||||
private class NewGcm extends StdLibNewEncrypter {
|
||||
NewGcm() {
|
||||
exists(string name | this.getTarget().hasQualifiedName("crypto/cipher", name) |
|
||||
name = ["NewGCM", "NewGCMWithNonceSize", "NewGCMWithRandomNonce", "NewGCMWithTagSize"]
|
||||
)
|
||||
}
|
||||
|
||||
override BlockMode getMode() { result = "GCM" }
|
||||
}
|
||||
|
||||
private class NewOfb extends StdLibNewEncrypter {
|
||||
NewOfb() { this.getTarget().hasQualifiedName("crypto/cipher", "NewOFB") }
|
||||
|
||||
override BlockMode getMode() { result = "OFB" }
|
||||
}
|
||||
|
||||
private class AeadSeal extends EncryptionMethodCall {
|
||||
AeadSeal() {
|
||||
this.(DataFlow::MethodCallNode)
|
||||
.getTarget()
|
||||
.hasQualifiedName("crypto/cipher", "AEAD", "Seal") and
|
||||
inputArg = 2
|
||||
}
|
||||
}
|
||||
|
||||
private class BlockEncrypt extends EncryptionMethodCall {
|
||||
BlockEncrypt() {
|
||||
this.(DataFlow::MethodCallNode)
|
||||
.getTarget()
|
||||
.hasQualifiedName("crypto/cipher", "Block", "Encrypt") and
|
||||
inputArg = 1
|
||||
}
|
||||
}
|
||||
|
||||
private class BlockModeCryptBlocks extends EncryptionMethodCall {
|
||||
BlockModeCryptBlocks() {
|
||||
this.(DataFlow::MethodCallNode)
|
||||
.getTarget()
|
||||
.hasQualifiedName("crypto/cipher", "BlockMode", "CryptBlocks") and
|
||||
inputArg = 1
|
||||
}
|
||||
}
|
||||
|
||||
private class StreamXorKeyStream extends EncryptionMethodCall {
|
||||
StreamXorKeyStream() {
|
||||
this.(DataFlow::MethodCallNode)
|
||||
.getTarget()
|
||||
.hasQualifiedName("crypto/cipher", "Stream", "XORKeyStream") and
|
||||
inputArg = 1
|
||||
}
|
||||
}
|
||||
|
||||
private class StreamReader extends EncryptionOperation {
|
||||
StreamReader() {
|
||||
lookThroughPointerType(this.getType()).hasQualifiedName("crypto/cipher", "StreamReader") and
|
||||
exists(DataFlow::Write w, DataFlow::Node base, Field f |
|
||||
f.hasQualifiedName("crypto/cipher", "StreamReader", "S") and
|
||||
w.writesField(base, f, encryptionFlowTarget) and
|
||||
DataFlow::localFlow(base, this)
|
||||
) and
|
||||
exists(DataFlow::Write w, DataFlow::Node base, Field f |
|
||||
f.hasQualifiedName("crypto/cipher", "StreamReader", "R") and
|
||||
w.writesField(base, f, inputNode) and
|
||||
DataFlow::localFlow(base, this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Limitation: StreamWriter wraps a Writer, so we need to look forward to
|
||||
* where the Writer is written to. Currently this is done using local flow,
|
||||
* so it only works within one function.
|
||||
*/
|
||||
private class StreamWriter extends EncryptionOperation {
|
||||
StreamWriter() {
|
||||
lookThroughPointerType(this.getType()).hasQualifiedName("crypto/cipher", "StreamWriter") and
|
||||
inputNode = this and
|
||||
exists(DataFlow::Write w, DataFlow::Node base, Field f |
|
||||
w.writesField(base, f, encryptionFlowTarget) and
|
||||
f.hasQualifiedName("crypto/cipher", "StreamWriter", "S")
|
||||
|
|
||||
base = this or
|
||||
TaintTracking::localTaint(base, this.(DataFlow::PostUpdateNode).getPreUpdateNode())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private module Hash {
|
||||
private class HashSum extends HashOperation instanceof DataFlow::MethodCallNode {
|
||||
HashSum() { this.getTarget().implements("hash", "Hash", "Sum") }
|
||||
|
||||
override HashAlgorithmInit getInitialization() { HashFlow::flow(result, super.getReceiver()) }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result = this.getInitialization().getAlgorithm() }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = super.getArgument(0) }
|
||||
}
|
||||
}
|
||||
|
||||
private DataFlow::Node getANonIoWriterPredecessor(DataFlow::Node node) {
|
||||
node.getType().implements("io", "Writer") and
|
||||
exists(DataFlow::Node pre | TaintTracking::localTaintStep(pre, node) |
|
||||
if pre.getType().implements("io", "Writer")
|
||||
then result = getANonIoWriterPredecessor(pre)
|
||||
else result = pre
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint flowing to an `io.Writer` (such as `hash.Hash` or `*sha3.SHAKE`) via
|
||||
* its implementation of the `io.Writer` interface.
|
||||
*/
|
||||
private class FlowToIoWriter extends HashOperation instanceof DataFlow::Node {
|
||||
HashAlgorithmInit init;
|
||||
DataFlow::Node input;
|
||||
|
||||
FlowToIoWriter() {
|
||||
this.getType().implements("io", "Writer") and
|
||||
HashFlow::flow(init, this) and
|
||||
// If we have `h.Write(taint)` or `io.WriteString(h, taint)` then it's
|
||||
// the post-update node of `h` that gets tainted.
|
||||
exists(DataFlow::PostUpdateNode pun | pun.getPreUpdateNode() = this |
|
||||
input = getANonIoWriterPredecessor(pun)
|
||||
)
|
||||
}
|
||||
|
||||
override HashAlgorithmInit getInitialization() { result = init }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result = this.getInitialization().getAlgorithm() }
|
||||
|
||||
override DataFlow::Node getAnInput() { result = input }
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently only weak algorithms from the `golang.org/x/crypto` module are
|
||||
* modeled here.
|
||||
*/
|
||||
private module GolangOrgXCrypto {
|
||||
private module Md4 {
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("golang.org/x/crypto/md4", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("MD4") }
|
||||
}
|
||||
}
|
||||
|
||||
private module Ripemd160 {
|
||||
private class New extends HashAlgorithmInit instanceof DataFlow::CallNode {
|
||||
New() { this.getTarget().hasQualifiedName("golang.org/x/crypto/ripemd160", "New") }
|
||||
|
||||
override HashingAlgorithm getAlgorithm() { result.matchesName("RIPEMD160") }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting "use of a
|
||||
* broken or weak cryptographic hashing algorithm on sensitive data"
|
||||
* vulnerabilities, as well as extension points for adding your own. This is
|
||||
* divided into two general cases:
|
||||
* - hashing sensitive data
|
||||
* - hashing passwords (which requires the hashing algorithm to be
|
||||
* sufficiently computationally expensive in addition to other requirements)
|
||||
*/
|
||||
|
||||
import go
|
||||
private import semmle.go.security.SensitiveActions
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting "use of a broken or weak
|
||||
* cryptographic hashing algorithm on sensitive data" vulnerabilities on sensitive data that does
|
||||
* NOT require computationally expensive hashing, as well as extension points for adding your own.
|
||||
*
|
||||
* Also see the `ComputationallyExpensiveHashFunction` module.
|
||||
*/
|
||||
module NormalHashFunction {
|
||||
/**
|
||||
* A data flow source for "use of a broken or weak cryptographic hashing algorithm on sensitive
|
||||
* data" vulnerabilities that does not require computationally expensive hashing. That is, a
|
||||
* piece of sensitive data that is not a password.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node {
|
||||
Source() { not this instanceof ComputationallyExpensiveHashFunction::Source }
|
||||
|
||||
/**
|
||||
* Gets the classification of the sensitive data.
|
||||
*/
|
||||
abstract string getClassification();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for "use of a broken or weak cryptographic hashing algorithm on sensitive
|
||||
* data" vulnerabilities that applies to data that does not require computationally expensive
|
||||
* hashing. That is, a broken or weak hashing algorithm.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the name of the weak hashing algorithm.
|
||||
*/
|
||||
abstract string getAlgorithmName();
|
||||
}
|
||||
|
||||
/**
|
||||
* A barrier for "use of a broken or weak cryptographic hashing algorithm on sensitive data"
|
||||
* vulnerabilities that applies to data that does not require computationally expensive hashing.
|
||||
*/
|
||||
abstract class Barrier extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A flow source modeled by the `SensitiveData` library.
|
||||
*/
|
||||
class SensitiveDataAsSource extends Source {
|
||||
SensitiveExpr::Classification classification;
|
||||
|
||||
SensitiveDataAsSource() {
|
||||
classification = this.asExpr().(SensitiveExpr).getClassification() and
|
||||
not classification = SensitiveExpr::password() and // (covered in ComputationallyExpensiveHashFunction)
|
||||
not classification = SensitiveExpr::id() // (not accurate enough)
|
||||
}
|
||||
|
||||
override SensitiveExpr::Classification getClassification() { result = classification }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow sink modeled by the `Cryptography` module.
|
||||
*/
|
||||
class WeakHashingOperationInputAsSink extends Sink {
|
||||
Cryptography::HashingAlgorithm algorithm;
|
||||
|
||||
WeakHashingOperationInputAsSink() {
|
||||
exists(Cryptography::CryptographicOperation operation |
|
||||
algorithm.isWeak() and
|
||||
algorithm = operation.getAlgorithm() and
|
||||
this = operation.getAnInput()
|
||||
)
|
||||
}
|
||||
|
||||
override string getAlgorithmName() { result = algorithm.getName() }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting "use of a broken or weak
|
||||
* cryptographic hashing algorithm on sensitive data" vulnerabilities on sensitive data that DOES
|
||||
* require computationally expensive hashing, as well as extension points for adding your own.
|
||||
*
|
||||
* Also see the `NormalHashFunction` module.
|
||||
*/
|
||||
module ComputationallyExpensiveHashFunction {
|
||||
/**
|
||||
* A data flow source for "use of a broken or weak cryptographic hashing algorithm on sensitive
|
||||
* data" vulnerabilities that does require computationally expensive hashing. That is, a
|
||||
* password.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the classification of the sensitive data.
|
||||
*/
|
||||
abstract string getClassification();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow sink for "use of a broken or weak cryptographic hashing algorithm on sensitive
|
||||
* data" vulnerabilities that applies to data that does require computationally expensive
|
||||
* hashing. That is, a broken or weak hashing algorithm or one that is not computationally
|
||||
* expensive enough for password hashing.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the name of the weak hashing algorithm.
|
||||
*/
|
||||
abstract string getAlgorithmName();
|
||||
|
||||
/**
|
||||
* Holds if this sink is for a computationally expensive hash function (meaning that hash
|
||||
* function is just weak in some other regard.
|
||||
*/
|
||||
abstract predicate isComputationallyExpensive();
|
||||
}
|
||||
|
||||
/**
|
||||
* A barrier for "use of a broken or weak cryptographic hashing algorithm on sensitive data"
|
||||
* vulnerabilities that applies to data that does require computationally expensive hashing.
|
||||
*/
|
||||
abstract class Barrier extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A flow source modeled by the `SensitiveData` library.
|
||||
*/
|
||||
class PasswordAsSource extends Source {
|
||||
SensitiveExpr::Classification classification;
|
||||
|
||||
PasswordAsSource() {
|
||||
classification = this.asExpr().(SensitiveExpr).getClassification() and
|
||||
classification = SensitiveExpr::password()
|
||||
}
|
||||
|
||||
override SensitiveExpr::Classification getClassification() { result = classification }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow sink modeled by the `Cryptography` module.
|
||||
*/
|
||||
class WeakPasswordHashingOperationInputSink extends Sink {
|
||||
Cryptography::CryptographicAlgorithm algorithm;
|
||||
|
||||
WeakPasswordHashingOperationInputSink() {
|
||||
exists(Cryptography::CryptographicOperation operation |
|
||||
(
|
||||
algorithm instanceof Cryptography::PasswordHashingAlgorithm and
|
||||
algorithm.isWeak()
|
||||
or
|
||||
algorithm instanceof Cryptography::HashingAlgorithm // Note that HashingAlgorithm and PasswordHashingAlgorithm are disjoint
|
||||
) and
|
||||
algorithm = operation.getAlgorithm() and
|
||||
this = operation.getAnInput()
|
||||
)
|
||||
}
|
||||
|
||||
override string getAlgorithmName() { result = algorithm.getName() }
|
||||
|
||||
override predicate isComputationallyExpensive() {
|
||||
algorithm instanceof Cryptography::PasswordHashingAlgorithm
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.4.9
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.4.8
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -28,10 +28,10 @@
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The following code uses the different packages to encrypt/hash
|
||||
some secret data. The first few examples uses DES, MD5, RC4, and SHA1,
|
||||
which are older algorithms that are now considered weak. The following
|
||||
examples use AES and SHA256, which are stronger, more modern algorithms.
|
||||
The following code uses the different packages to encrypt
|
||||
some secret data. The first example uses DES,
|
||||
which is an older algorithm that is now considered weak. The following
|
||||
example uses AES, which is a stronger, more modern algorithm.
|
||||
</p>
|
||||
|
||||
<sample src="examples/Crypto.go" />
|
||||
33
go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql
Normal file
33
go/ql/src/Security/CWE-327/BrokenCryptoAlgorithm.ql
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @name Use of a broken or weak cryptographic algorithm
|
||||
* @description Using broken or weak cryptographic algorithms can compromise security.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
* @id go/weak-cryptographic-algorithm
|
||||
* @tags security
|
||||
* external/cwe/cwe-327
|
||||
* external/cwe/cwe-328
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
from Cryptography::CryptographicOperation operation, string msgPrefix, DataFlow::Node init
|
||||
where
|
||||
init = operation.getInitialization() and
|
||||
// `init` may be a `BlockModeInit`, a `EncryptionAlgorithmInit`, or `operation` itself.
|
||||
(
|
||||
not init instanceof BlockModeInit and
|
||||
exists(Cryptography::CryptographicAlgorithm algorithm |
|
||||
algorithm = operation.getAlgorithm() and
|
||||
algorithm.isWeak() and
|
||||
msgPrefix = "The cryptographic algorithm " + algorithm.getName() and
|
||||
not algorithm instanceof Cryptography::HashingAlgorithm
|
||||
)
|
||||
or
|
||||
not init instanceof EncryptionAlgorithmInit and
|
||||
operation.getBlockMode().isWeak() and
|
||||
msgPrefix = "The block mode " + operation.getBlockMode()
|
||||
)
|
||||
select operation, "$@ is broken or weak, and should not be used.", init, msgPrefix
|
||||
97
go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp
Normal file
97
go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.qhelp
Normal file
@@ -0,0 +1,97 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Using a broken or weak cryptographic hash function can leave data
|
||||
vulnerable, and should not be used in security related code.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
A strong cryptographic hash function should be resistant to:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
pre-image attacks: if you know a hash value <code>h(x)</code>,
|
||||
you should not be able to easily find the input <code>x</code>.
|
||||
</li>
|
||||
<li>
|
||||
collision attacks: if you know a hash value <code>h(x)</code>,
|
||||
you should not be able to easily find a different input <code>y</code>
|
||||
with the same hash value <code>h(x) = h(y)</code>.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
In cases with a limited input space, such as for passwords, the hash
|
||||
function also needs to be computationally expensive to be resistant to
|
||||
brute-force attacks. Passwords should also have an unique salt applied
|
||||
before hashing, but that is not considered by this query.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
As an example, both MD5 and SHA-1 are known to be vulnerable to collision attacks.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Since it's OK to use a weak cryptographic hash function in a non-security
|
||||
context, this query only alerts when these are used to hash sensitive
|
||||
data (such as passwords, certificates, usernames).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Use of broken or weak cryptographic algorithms that are not hashing algorithms, is
|
||||
handled by the <code>rb/weak-cryptographic-algorithm</code> query.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Ensure that you use a strong, modern cryptographic hash function:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
such as Argon2, scrypt, bcrypt, or PBKDF2 for passwords and other data with limited input space.
|
||||
</li>
|
||||
<li>
|
||||
such as SHA-2, or SHA-3 in other cases.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
The following example shows two functions for checking whether the hash
|
||||
of a secret matches a known value.
|
||||
|
||||
The first function uses SHA-1 that is known to be vulnerable to collision attacks.
|
||||
|
||||
The second function uses SHA-256 that is a strong cryptographic hashing function.
|
||||
</p>
|
||||
|
||||
<sample src="examples/WeakSecretHashing.go" />
|
||||
|
||||
</example>
|
||||
<example>
|
||||
<p>
|
||||
The following example shows two functions for hashing passwords.
|
||||
|
||||
The first example uses SHA-256 to hash passwords. Although
|
||||
SHA-256 is a strong cryptographic hash function, it is not suitable for password
|
||||
hashing since it is not computationally expensive.
|
||||
|
||||
The second function uses PBKDF2, which is a strong password hashing algorithm.
|
||||
</p>
|
||||
|
||||
<sample src="examples/WeakPasswordHashing.go" />
|
||||
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html">Password Storage Cheat Sheet</a></li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
118
go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql
Normal file
118
go/ql/src/Security/CWE-327/WeakSensitiveDataHashing.ql
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* @name Use of a broken or weak cryptographic hashing algorithm on sensitive data
|
||||
* @description Using broken or weak cryptographic hashing algorithms can compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.5
|
||||
* @precision high
|
||||
* @id go/weak-sensitive-data-hashing
|
||||
* @tags security
|
||||
* external/cwe/cwe-327
|
||||
* external/cwe/cwe-328
|
||||
* external/cwe/cwe-916
|
||||
*/
|
||||
|
||||
import go
|
||||
import semmle.go.security.WeakSensitiveDataHashingCustomizations
|
||||
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting use of a broken or weak
|
||||
* cryptographic hash function on sensitive data, that does NOT require a
|
||||
* computationally expensive hash function.
|
||||
*/
|
||||
module NormalHashFunctionFlow {
|
||||
import NormalHashFunction
|
||||
|
||||
private module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Barrier }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node node) {
|
||||
// make sources barriers so that we only report the closest instance
|
||||
isSource(node)
|
||||
}
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) {
|
||||
// make sinks barriers so that we only report the closest instance
|
||||
isSink(node)
|
||||
}
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
}
|
||||
|
||||
import TaintTracking::Global<Config>
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a taint-tracking configuration for detecting use of a broken or weak
|
||||
* cryptographic hashing algorithm on passwords.
|
||||
*
|
||||
* Passwords has stricter requirements on the hashing algorithm used (must be
|
||||
* computationally expensive to prevent brute-force attacks).
|
||||
*/
|
||||
module ComputationallyExpensiveHashFunctionFlow {
|
||||
import ComputationallyExpensiveHashFunction
|
||||
|
||||
private module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Barrier }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node node) {
|
||||
// make sources barriers so that we only report the closest instance
|
||||
isSource(node)
|
||||
}
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) {
|
||||
// make sinks barriers so that we only report the closest instance
|
||||
isSink(node)
|
||||
}
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
}
|
||||
|
||||
import TaintTracking::Global<Config>
|
||||
}
|
||||
|
||||
/**
|
||||
* Global taint-tracking for detecting both variants of "use of a broken or weak
|
||||
* cryptographic hashing algorithm on sensitive data" vulnerabilities. The two configurations are
|
||||
* merged to generate a combined path graph.
|
||||
*/
|
||||
module WeakSensitiveDataHashingFlow =
|
||||
DataFlow::MergePathGraph<NormalHashFunctionFlow::PathNode,
|
||||
ComputationallyExpensiveHashFunctionFlow::PathNode, NormalHashFunctionFlow::PathGraph,
|
||||
ComputationallyExpensiveHashFunctionFlow::PathGraph>;
|
||||
|
||||
import WeakSensitiveDataHashingFlow::PathGraph
|
||||
|
||||
from
|
||||
WeakSensitiveDataHashingFlow::PathNode source, WeakSensitiveDataHashingFlow::PathNode sink,
|
||||
string ending, string algorithmName, string classification
|
||||
where
|
||||
NormalHashFunctionFlow::flowPath(source.asPathNode1(), sink.asPathNode1()) and
|
||||
algorithmName = sink.getNode().(NormalHashFunction::Sink).getAlgorithmName() and
|
||||
classification = source.getNode().(NormalHashFunction::Source).getClassification() and
|
||||
ending = "."
|
||||
or
|
||||
ComputationallyExpensiveHashFunctionFlow::flowPath(source.asPathNode2(), sink.asPathNode2()) and
|
||||
algorithmName = sink.getNode().(ComputationallyExpensiveHashFunction::Sink).getAlgorithmName() and
|
||||
classification =
|
||||
source.getNode().(ComputationallyExpensiveHashFunction::Source).getClassification() and
|
||||
(
|
||||
sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
|
||||
ending = "."
|
||||
or
|
||||
not sink.getNode().(ComputationallyExpensiveHashFunction::Sink).isComputationallyExpensive() and
|
||||
ending =
|
||||
" for " + classification +
|
||||
" hashing, since it is not a computationally expensive hash function."
|
||||
)
|
||||
select sink.getNode(), source, sink,
|
||||
"$@ is used in a hashing algorithm (" + algorithmName + ") that is insecure" + ending,
|
||||
source.getNode(), "Sensitive data (" + classification + ")"
|
||||
20
go/ql/src/Security/CWE-327/examples/Crypto.go
Normal file
20
go/ql/src/Security/CWE-327/examples/Crypto.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/des"
|
||||
)
|
||||
|
||||
func EncryptMessageWeak(key []byte, message []byte) (dst []byte) {
|
||||
// BAD, DES is a weak crypto algorithm
|
||||
block, _ := des.NewCipher(key)
|
||||
block.Encrypt(dst, message)
|
||||
return
|
||||
}
|
||||
|
||||
func EncryptMessageStrong(key []byte, message []byte) (dst []byte) {
|
||||
// GOOD, AES is a weak crypto algorithm
|
||||
block, _ := aes.NewCipher(key)
|
||||
block.Encrypt(dst, message)
|
||||
return
|
||||
}
|
||||
21
go/ql/src/Security/CWE-327/examples/WeakPasswordHashing.go
Normal file
21
go/ql/src/Security/CWE-327/examples/WeakPasswordHashing.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/pbkdf2"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
)
|
||||
|
||||
func GetPasswordHashBad(password string) [32]byte {
|
||||
// BAD, SHA256 is a strong hashing algorithm but it is not computationally expensive
|
||||
return sha256.Sum256([]byte(password))
|
||||
}
|
||||
|
||||
func GetPasswordHashGood(password string) []byte {
|
||||
// GOOD, PBKDF2 is a strong hashing algorithm and it is computationally expensive
|
||||
salt := make([]byte, 16)
|
||||
rand.Read(salt)
|
||||
key, _ := pbkdf2.Key(sha512.New, password, salt, 4096, 32)
|
||||
return key
|
||||
}
|
||||
19
go/ql/src/Security/CWE-327/examples/WeakSecretHashing.go
Normal file
19
go/ql/src/Security/CWE-327/examples/WeakSecretHashing.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"slices"
|
||||
)
|
||||
|
||||
func SecretMatchesKnownHashBad(secret []byte, known_hash []byte) bool {
|
||||
// BAD, SHA1 is a weak crypto algorithm and secret is sensitive data
|
||||
h := sha1.New()
|
||||
return slices.Equal(h.Sum(secret), known_hash)
|
||||
}
|
||||
|
||||
func SecretMatchesKnownHashGood(secret []byte, known_hash []byte) bool {
|
||||
// GOOD, SHA256 is a strong hashing algorithm
|
||||
h := sha256.New()
|
||||
return slices.Equal(h.Sum(secret), known_hash)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `go/weak-crypto-algorithm`, to detect the use of a broken or weak cryptographic algorithm. A very simple version of this query was originally contributed as an [experimental query by @dilanbhalla](https://github.com/github/codeql-go/pull/284).
|
||||
* Added a new query, `go/weak-sensitive-data-hashing`, to detect the use of a broken or weak cryptographic hash algorithm on sensitive data.
|
||||
3
go/ql/src/change-notes/released/1.4.9.md
Normal file
3
go/ql/src/change-notes/released/1.4.9.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.4.9
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.4.8
|
||||
lastReleaseVersion: 1.4.9
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
/**
|
||||
* Provides classes for modeling cryptographic libraries.
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
/**
|
||||
* Names of cryptographic algorithms, separated into strong and weak variants.
|
||||
*
|
||||
* The names are normalized: upper-case, no spaces, dashes or underscores.
|
||||
*
|
||||
* The names are inspired by the names used in real world crypto libraries.
|
||||
*
|
||||
* The classification into strong and weak are based on OWASP and Wikipedia (2020).
|
||||
*
|
||||
* Sources (more links in qhelp file):
|
||||
* https://en.wikipedia.org/wiki/Strong_cryptography#Cryptographically_strong_algorithms
|
||||
* https://en.wikipedia.org/wiki/Strong_cryptography#Examples
|
||||
* https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html
|
||||
*/
|
||||
private module AlgorithmNames {
|
||||
predicate isStrongHashingAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"DSA", "ED25519", "SHA256", "SHA384", "SHA512", "SHA3", "ES256", "ECDSA256", "ES384",
|
||||
"ECDSA384", "ES512", "ECDSA512", "SHA2", "SHA224"
|
||||
]
|
||||
}
|
||||
|
||||
predicate isWeakHashingAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"HAVEL128", "MD2", "SHA1", "MD4", "MD5", "PANAMA", "RIPEMD", "RIPEMD128", "RIPEMD256",
|
||||
"RIPEMD320", "SHA0"
|
||||
]
|
||||
}
|
||||
|
||||
predicate isStrongEncryptionAlgorithm(string name) {
|
||||
name = ["AES", "AES128", "AES192", "AES256", "AES512", "RSA", "RABBIT", "BLOWFISH"]
|
||||
}
|
||||
|
||||
predicate isWeakEncryptionAlgorithm(string name) {
|
||||
name =
|
||||
[
|
||||
"DES", "3DES", "ARC5", "RC5", "TRIPLEDES", "TDEA", "TRIPLEDEA", "ARC2", "RC2", "ARC4",
|
||||
"RC4", "ARCFOUR"
|
||||
]
|
||||
}
|
||||
|
||||
predicate isStrongPasswordHashingAlgorithm(string name) {
|
||||
name = ["ARGON2", "PBKDF2", "BCRYPT", "SCRYPT"]
|
||||
}
|
||||
|
||||
predicate isWeakPasswordHashingAlgorithm(string name) { none() }
|
||||
}
|
||||
|
||||
private import AlgorithmNames
|
||||
|
||||
/**
|
||||
* A cryptographic algorithm.
|
||||
*/
|
||||
private newtype TCryptographicAlgorithm =
|
||||
MkHashingAlgorithm(string name, boolean isWeak) {
|
||||
isStrongHashingAlgorithm(name) and isWeak = false
|
||||
or
|
||||
isWeakHashingAlgorithm(name) and isWeak = true
|
||||
} or
|
||||
MkEncryptionAlgorithm(string name, boolean isWeak) {
|
||||
isStrongEncryptionAlgorithm(name) and isWeak = false
|
||||
or
|
||||
isWeakEncryptionAlgorithm(name) and isWeak = true
|
||||
} or
|
||||
MkPasswordHashingAlgorithm(string name, boolean isWeak) {
|
||||
isStrongPasswordHashingAlgorithm(name) and isWeak = false
|
||||
or
|
||||
isWeakPasswordHashingAlgorithm(name) and isWeak = true
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic algorithm.
|
||||
*/
|
||||
abstract class CryptographicAlgorithm extends TCryptographicAlgorithm {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getName() }
|
||||
|
||||
/**
|
||||
* Gets the name of this algorithm.
|
||||
*/
|
||||
abstract string getName();
|
||||
|
||||
/**
|
||||
* Holds if the name of this algorithm matches `name` modulo case,
|
||||
* white space, dashes and underscores.
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate matchesName(string name) {
|
||||
exists(name.regexpReplaceAll("[-_]", "").regexpFind("(?i)\\Q" + this.getName() + "\\E", _, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this algorithm is weak.
|
||||
*/
|
||||
abstract predicate isWeak();
|
||||
}
|
||||
|
||||
/**
|
||||
* A hashing algorithm such as `MD5` or `SHA512`.
|
||||
*/
|
||||
class HashingAlgorithm extends MkHashingAlgorithm, CryptographicAlgorithm {
|
||||
string name;
|
||||
boolean isWeak;
|
||||
|
||||
HashingAlgorithm() { this = MkHashingAlgorithm(name, isWeak) }
|
||||
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* An encryption algorithm such as `DES` or `AES512`.
|
||||
*/
|
||||
class EncryptionAlgorithm extends MkEncryptionAlgorithm, CryptographicAlgorithm {
|
||||
string name;
|
||||
boolean isWeak;
|
||||
|
||||
EncryptionAlgorithm() { this = MkEncryptionAlgorithm(name, isWeak) }
|
||||
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* A password hashing algorithm such as `PBKDF2` or `SCRYPT`.
|
||||
*/
|
||||
class PasswordHashingAlgorithm extends MkPasswordHashingAlgorithm, CryptographicAlgorithm {
|
||||
string name;
|
||||
boolean isWeak;
|
||||
|
||||
PasswordHashingAlgorithm() { this = MkPasswordHashingAlgorithm(name, isWeak) }
|
||||
|
||||
override string getName() { result = name }
|
||||
|
||||
override predicate isWeak() { isWeak = true }
|
||||
}
|
||||
|
||||
/**
|
||||
* An application of a cryptographic algorithm.
|
||||
*/
|
||||
abstract class CryptographicOperation extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the input the algorithm is used on, e.g. the plain text input to be encrypted.
|
||||
*/
|
||||
abstract Expr getInput();
|
||||
|
||||
/**
|
||||
* Gets the applied algorithm.
|
||||
*/
|
||||
abstract CryptographicAlgorithm getAlgorithm();
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic operation from the `crypto/md5` package.
|
||||
*/
|
||||
class Md5 extends CryptographicOperation, DataFlow::CallNode {
|
||||
Md5() { this.getTarget().hasQualifiedName("crypto/md5", ["New", "Sum"]) }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic operation from the `crypto/sha1` package.
|
||||
*/
|
||||
class Sha1 extends CryptographicOperation, DataFlow::CallNode {
|
||||
Sha1() { this.getTarget().hasQualifiedName("crypto/sha1", ["New", "Sum"]) }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic operation from the `crypto/des` package.
|
||||
*/
|
||||
class Des extends CryptographicOperation, DataFlow::CallNode {
|
||||
Des() { this.getTarget().hasQualifiedName("crypto/des", ["NewCipher", "NewTripleDESCipher"]) }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cryptographic operation from the `crypto/rc4` package.
|
||||
*/
|
||||
class Rc4 extends CryptographicOperation, DataFlow::CallNode {
|
||||
Rc4() { this.getTarget().hasQualifiedName("crypto/rc4", "NewCipher") }
|
||||
|
||||
override Expr getInput() { result = this.getArgument(0).asExpr() }
|
||||
|
||||
override CryptographicAlgorithm getAlgorithm() {
|
||||
result.matchesName(this.getTarget().getPackage().getName())
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* @name Use of a weak cryptographic algorithm
|
||||
* @description Using weak cryptographic algorithms can allow an attacker to compromise security.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @id go/weak-crypto-algorithm
|
||||
* @tags security
|
||||
* experimental
|
||||
* external/cwe/cwe-327
|
||||
* external/cwe/cwe-328
|
||||
*/
|
||||
|
||||
import go
|
||||
import WeakCryptoAlgorithmCustomizations
|
||||
import WeakCryptoAlgorithm::Flow::PathGraph
|
||||
|
||||
from WeakCryptoAlgorithm::Flow::PathNode source, WeakCryptoAlgorithm::Flow::PathNode sink
|
||||
where WeakCryptoAlgorithm::Flow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "$@ is used in a weak cryptographic algorithm.",
|
||||
source.getNode(), "Sensitive data"
|
||||
@@ -1,66 +0,0 @@
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for reasoning about
|
||||
* sensitive information in weak cryptographic algorithms,
|
||||
* as well as extension points for adding your own.
|
||||
*/
|
||||
|
||||
import go
|
||||
private import semmle.go.security.SensitiveActions
|
||||
private import CryptoLibraries
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for reasoning about
|
||||
* sensitive information in weak cryptographic algorithms,
|
||||
* as well as extension points for adding your own.
|
||||
*/
|
||||
module WeakCryptoAlgorithm {
|
||||
/**
|
||||
* A data flow source for sensitive information in weak cryptographic algorithms.
|
||||
*/
|
||||
abstract class Source extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A data flow sink for sensitive information in weak cryptographic algorithms.
|
||||
*/
|
||||
abstract class Sink extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sanitizer for sensitive information in weak cryptographic algorithms.
|
||||
*/
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A sensitive source.
|
||||
*/
|
||||
class SensitiveSource extends Source {
|
||||
SensitiveSource() { this.asExpr() instanceof SensitiveExpr }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression used by a weak cryptographic algorithm.
|
||||
*/
|
||||
class WeakCryptographicOperationSink extends Sink {
|
||||
WeakCryptographicOperationSink() {
|
||||
exists(CryptographicOperation application |
|
||||
application.getAlgorithm().isWeak() and
|
||||
this.asExpr() = application.getInput()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks taint flow from sensitive information to weak cryptographic
|
||||
* algorithms.
|
||||
*/
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/des"
|
||||
"crypto/md5"
|
||||
"crypto/rc4"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
)
|
||||
|
||||
func main() {
|
||||
public := []byte("hello")
|
||||
|
||||
password := []byte("123456")
|
||||
buf := password // testing dataflow by passing into different variable
|
||||
|
||||
// BAD, des is a weak crypto algorithm and password is sensitive data
|
||||
des.NewTripleDESCipher(buf)
|
||||
|
||||
// BAD, md5 is a weak crypto algorithm and password is sensitive data
|
||||
md5.Sum(buf)
|
||||
|
||||
// BAD, rc4 is a weak crypto algorithm and password is sensitive data
|
||||
rc4.NewCipher(buf)
|
||||
|
||||
// BAD, sha1 is a weak crypto algorithm and password is sensitive data
|
||||
sha1.Sum(buf)
|
||||
|
||||
// GOOD, password is sensitive data but aes is a strong crypto algorithm
|
||||
aes.NewCipher(buf)
|
||||
|
||||
// GOOD, password is sensitive data but sha256 is a strong crypto algorithm
|
||||
sha256.Sum256(buf)
|
||||
|
||||
// GOOD, des is a weak crypto algorithm but public is not sensitive data
|
||||
des.NewTripleDESCipher(public)
|
||||
|
||||
// GOOD, md5 is a weak crypto algorithm but public is not sensitive data
|
||||
md5.Sum(public)
|
||||
|
||||
// GOOD, rc4 is a weak crypto algorithm but public is not sensitive data
|
||||
rc4.NewCipher(public)
|
||||
|
||||
// GOOD, sha1 is a weak crypto algorithm but public is not sensitive data
|
||||
sha1.Sum(public)
|
||||
|
||||
// GOOD, aes is a strong crypto algorithm and public is not sensitive data
|
||||
aes.NewCipher(public)
|
||||
|
||||
// GOOD, sha256 is a strong crypto algorithm and public is not sensitive data
|
||||
sha256.Sum256(public)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/go-queries
|
||||
version: 1.4.9-dev
|
||||
version: 1.4.10-dev
|
||||
groups:
|
||||
- go
|
||||
- queries
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user