Merge branch 'main' into 20823-globalVarRef-document-defaultView

This commit is contained in:
Eliav2
2025-11-20 15:18:15 +02:00
committed by GitHub
487 changed files with 23401 additions and 10311 deletions

3
.gitattributes vendored
View File

@@ -82,9 +82,6 @@
/csharp/paket.main.bzl linguist-generated=true /csharp/paket.main.bzl linguist-generated=true
/csharp/paket.main_extension.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 prebuilt resources
/swift/third_party/resources/*.zip filter=lfs diff=lfs merge=lfs -text /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 /swift/third_party/resources/*.tar.zst filter=lfs diff=lfs merge=lfs -text

View File

@@ -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 }}

View File

@@ -269,24 +269,16 @@ go_deps = use_extension("@gazelle//:extensions.bzl", "go_deps")
go_deps.from_file(go_mod = "//go/extractor:go.mod") go_deps.from_file(go_mod = "//go/extractor:go.mod")
use_repo(go_deps, "org_golang_x_mod", "org_golang_x_tools") 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( # go to https://github.com/GoogleChrome/ripunzip/releases to find latest version and corresponding sha256s
name = "ripunzip-linux", ripunzip_archive(
src = "//misc/ripunzip:ripunzip-Linux.tar.zst", name = "ripunzip",
build_file = "//misc/ripunzip:BUILD.ripunzip.bazel", sha256_linux = "ee0e8a957687a5dc3a66b2a4b25883bf762df4c9c07f0651af527a32a405054b",
) sha256_macos_arm = "8a88eea54eac232d162a72a42065e0429b82dbf4f05e9642915dff9d7a81f846",
sha256_macos_intel = "4457a18bfcc5feabe09f5ea3d1157128e07b4873392cb404a870e611924abf64",
lfs_archive( sha256_windows = "66d0c1375301bf5ab815348048f43b110631d3fa7200acd50d50a8ed8655ca62",
name = "ripunzip-windows", version = "2.0.3",
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",
) )
register_toolchains( register_toolchains(

View File

@@ -1,3 +1,7 @@
## 0.4.21
No user-facing changes.
## 0.4.20 ## 0.4.20
No user-facing changes. No user-facing changes.

View File

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

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 0.4.20 lastReleaseVersion: 0.4.21

View File

@@ -1,5 +1,5 @@
name: codeql/actions-all name: codeql/actions-all
version: 0.4.21-dev version: 0.4.22-dev
library: true library: true
warnOnImplicitThis: true warnOnImplicitThis: true
dependencies: dependencies:

View File

@@ -1,3 +1,7 @@
## 0.6.13
No user-facing changes.
## 0.6.12 ## 0.6.12
No user-facing changes. No user-facing changes.

View File

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

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 0.6.12 lastReleaseVersion: 0.6.13

View File

@@ -1,5 +1,5 @@
name: codeql/actions-queries name: codeql/actions-queries
version: 0.6.13-dev version: 0.6.14-dev
library: false library: false
warnOnImplicitThis: true warnOnImplicitThis: true
groups: [actions, queries] groups: [actions, queries]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
description: Support expanded compilation argument lists
compatibility: full
compilation_expanded_args.rel: delete

View File

@@ -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 ## 6.0.1
No user-facing changes. No user-facing changes.

View File

@@ -1,4 +0,0 @@
---
category: fix
---
* Improve performance of the range analysis in cases where it would otherwise take an exorbitant amount of time.

View 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`.

View 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.

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 6.0.1 lastReleaseVersion: 6.1.0

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all name: codeql/cpp-all
version: 6.0.2-dev version: 6.1.1-dev
groups: cpp groups: cpp
dbscheme: semmlecode.cpp.dbscheme dbscheme: semmlecode.cpp.dbscheme
extractor: cpp extractor: cpp

View File

@@ -94,6 +94,25 @@ class Compilation extends @compilation {
*/ */
string getArgument(int i) { compilation_args(this, i, result) } 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 * Gets the total amount of CPU time spent processing all the files in the
* front-end and extractor. * front-end and extractor.

View File

@@ -171,12 +171,14 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
* Gets the nth parameter of this function. There is no result for the * Gets the nth parameter of this function. There is no result for the
* implicit `this` parameter, and there is no `...` varargs pseudo-parameter. * implicit `this` parameter, and there is no `...` varargs pseudo-parameter.
*/ */
pragma[nomagic]
Parameter getParameter(int n) { params(unresolveElement(result), underlyingElement(this), n, _) } Parameter getParameter(int n) { params(unresolveElement(result), underlyingElement(this), n, _) }
/** /**
* Gets a parameter of this function. There is no result for the implicit * Gets a parameter of this function. There is no result for the implicit
* `this` parameter, and there is no `...` varargs pseudo-parameter. * `this` parameter, and there is no `...` varargs pseudo-parameter.
*/ */
pragma[nomagic]
Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) } Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) }
/** /**

View File

@@ -750,6 +750,16 @@ class SizeofPackTypeOperator extends SizeofPackOperator {
*/ */
class SizeofOperator extends Expr, @runtime_sizeof { class SizeofOperator extends Expr, @runtime_sizeof {
override int getPrecedence() { result = 16 } 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. */ /** Gets the contained expression. */
Expr getExprOperand() { result = this.getChild(0) } Expr getExprOperand() { result = this.getChild(0) }
override Type getTypeOperand() { result = this.getExprOperand().getType() }
override string toString() { result = "sizeof(<expr>)" } override string toString() { result = "sizeof(<expr>)" }
override predicate mayBeImpure() { this.getExprOperand().mayBeImpure() } override predicate mayBeImpure() { this.getExprOperand().mayBeImpure() }
@@ -784,8 +796,7 @@ class SizeofTypeOperator extends SizeofOperator {
override string getAPrimaryQlClass() { result = "SizeofTypeOperator" } override string getAPrimaryQlClass() { result = "SizeofTypeOperator" }
/** Gets the contained type. */ override Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) }
Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) }
override string toString() { result = "sizeof(" + this.getTypeOperand().getName() + ")" } override string toString() { result = "sizeof(" + this.getTypeOperand().getName() + ")" }
@@ -842,6 +853,16 @@ class AlignofTypeOperator extends AlignofOperator {
*/ */
class DatasizeofOperator extends Expr, @datasizeof { class DatasizeofOperator extends Expr, @datasizeof {
override int getPrecedence() { result = 16 } 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. */ /** Gets the contained expression. */
Expr getExprOperand() { result = this.getChild(0) } Expr getExprOperand() { result = this.getChild(0) }
override Type getTypeOperand() { result = this.getExprOperand().getType() }
override string toString() { result = "__datasizeof(<expr>)" } override string toString() { result = "__datasizeof(<expr>)" }
override predicate mayBeImpure() { this.getExprOperand().mayBeImpure() } override predicate mayBeImpure() { this.getExprOperand().mayBeImpure() }
@@ -870,8 +893,7 @@ class DatasizeofTypeOperator extends DatasizeofOperator {
override string getAPrimaryQlClass() { result = "DatasizeofTypeOperator" } override string getAPrimaryQlClass() { result = "DatasizeofTypeOperator" }
/** Gets the contained type. */ override Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) }
Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) }
override string toString() { result = "__datasizeof(" + this.getTypeOperand().getName() + ")" } override string toString() { result = "__datasizeof(" + this.getTypeOperand().getName() + ")" }

View File

@@ -861,6 +861,10 @@ predicate jumpStep(Node n1, Node n2) {
n2.(FlowSummaryNode).getSummaryNode()) 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`. * 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 * 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) { predicate storeStepImpl(Node node1, Content c, Node node2, boolean certain) {
exists( exists(
PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads, PostFieldUpdateNode postFieldUpdate, int indirectionIndex1, int numberOfLoads,
StoreInstruction store StoreInstruction store, FieldContent fc
| |
postFieldUpdate = node2 and 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 postFieldUpdate.getIndirectionIndex() = 1 and
numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(), numberOfLoadsFromOperand(postFieldUpdate.getFieldAddress(),
store.getDestinationAddressOperand(), numberOfLoads, certain) store.getDestinationAddressOperand(), numberOfLoads, certain) and
| fc.getAField() = postFieldUpdate.getUpdatedField() and
exists(FieldContent fc | fc = c | getIndirectionIndexLate(fc) = 1 + indirectionIndex1 + numberOfLoads
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
)
) )
or or
// models-as-data summarized flow // models-as-data summarized flow
@@ -965,22 +963,17 @@ predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex)
* `node2`. * `node2`.
*/ */
predicate readStep(Node node1, ContentSet c, Node 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 nodeHasOperand(node2, operand, indirectionIndex2) and
// The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct // The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct
// in `storeStep`. // in `storeStep`.
nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and
numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) and
| fc.getAField() = fa1.getField() and
exists(FieldContent fc | fc = c | getIndirectionIndexLate(fc) = indirectionIndex2 + numberOfLoads
fc.getField() = fa1.getField() and
fc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
)
or
exists(UnionContent uc | uc = c |
uc.getAField() = fa1.getField() and
uc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads
)
) )
or or
// models-as-data summarized flow // models-as-data summarized flow
@@ -1574,7 +1567,7 @@ pragma[inline]
ContentApprox getContentApprox(Content c) { ContentApprox getContentApprox(Content c) {
exists(string prefix, Field f | exists(string prefix, Field f |
prefix = result.(FieldApproxContent).getPrefix() and prefix = result.(FieldApproxContent).getPrefix() and
f = c.(FieldContent).getField() and f = c.(NonUnionFieldContent).getField() and
fieldHasApproxName(f, prefix) fieldHasApproxName(f, prefix)
) )
or or

View File

@@ -2093,8 +2093,8 @@ private Field getAFieldWithSize(Union u, int bytes) {
cached cached
private newtype TContent = private newtype TContent =
TFieldContent(Field f, int indirectionIndex) { TNonUnionContent(Field f, int indirectionIndex) {
// the indirection index for field content starts at 1 (because `TFieldContent` is thought of as // the indirection index for field content starts at 1 (because `TNonUnionContent` is thought of as
// the address of the field, `FieldAddress` in the IR). // the address of the field, `FieldAddress` in the IR).
indirectionIndex = [1 .. SsaImpl::getMaxIndirectionsForType(f.getUnspecifiedType())] and indirectionIndex = [1 .. SsaImpl::getMaxIndirectionsForType(f.getUnspecifiedType())] and
// Reads and writes of union fields are tracked using `UnionContent`. // Reads and writes of union fields are tracked using `UnionContent`.
@@ -2124,14 +2124,14 @@ private newtype TContent =
*/ */
class Content extends TContent { class Content extends TContent {
/** Gets a textual representation of this element. */ /** 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) { 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 path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
} }
/** Gets the indirection index of this `Content`. */ /** Gets the indirection index of this `Content`. */
abstract int getIndirectionIndex(); int getIndirectionIndex() { none() } // overridden in subclasses
/** /**
* INTERNAL: Do not use. * 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 * For example, a write to a field `f` implies that any content of
* the form `*f` is also cleared. * 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 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 { 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 Field f;
private int indirectionIndex; private int indirectionIndex;
FieldContent() { this = TFieldContent(f, indirectionIndex) } NonUnionFieldContent() { this = TNonUnionContent(f, indirectionIndex) }
override string toString() { result = contentStars(this) + f.toString() } override string toString() { result = contentStars(this) + f.toString() }
Field getField() { result = f } override Field getAField() { result = f }
/** Gets the indirection index of this `FieldContent`. */ /** Gets the indirection index of this `FieldContent`. */
pragma[inline] override int getIndirectionIndex() { result = indirectionIndex }
override int getIndirectionIndex() {
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
}
override predicate impliesClearOf(Content c) { override predicate impliesClearOf(Content c) {
exists(FieldContent fc | exists(FieldContent fc |
@@ -2191,7 +2211,7 @@ class FieldContent extends Content, TFieldContent {
} }
/** A reference through an instance field of a union. */ /** A reference through an instance field of a union. */
class UnionContent extends Content, TUnionContent { class UnionContent extends FieldContent, TUnionContent {
private Union u; private Union u;
private int indirectionIndex; private int indirectionIndex;
private int bytes; private int bytes;
@@ -2201,16 +2221,13 @@ class UnionContent extends Content, TUnionContent {
override string toString() { result = contentStars(this) + u.toString() } override string toString() { result = contentStars(this) + u.toString() }
/** Gets a field of the underlying union of this `UnionContent`, if any. */ /** 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`. */ /** Gets the underlying union of this `UnionContent`. */
Union getUnion() { result = u } Union getUnion() { result = u }
/** Gets the indirection index of this `UnionContent`. */ /** Gets the indirection index of this `UnionContent`. */
pragma[inline] override int getIndirectionIndex() { result = indirectionIndex }
override int getIndirectionIndex() {
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
}
override predicate impliesClearOf(Content c) { override predicate impliesClearOf(Content c) {
exists(UnionContent uc | exists(UnionContent uc |
@@ -2234,10 +2251,7 @@ class ElementContent extends Content, TElementContent {
ElementContent() { this = TElementContent(indirectionIndex) } ElementContent() { this = TElementContent(indirectionIndex) }
pragma[inline] override int getIndirectionIndex() { result = indirectionIndex }
override int getIndirectionIndex() {
pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex)
}
override predicate impliesClearOf(Content c) { none() } override predicate impliesClearOf(Content c) { none() }

View File

@@ -47,6 +47,19 @@ compilation_args(
string arg : string ref 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. * 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

View File

@@ -0,0 +1,2 @@
description: Support expanded compilation argument lists
compatibility: backwards

View File

@@ -1,3 +1,7 @@
## 1.5.4
No user-facing changes.
## 1.5.3 ## 1.5.3
No user-facing changes. No user-facing changes.

View File

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

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 1.5.3 lastReleaseVersion: 1.5.4

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries name: codeql/cpp-queries
version: 1.5.4-dev version: 1.5.5-dev
groups: groups:
- cpp - cpp
- queries - queries

View File

@@ -190,7 +190,7 @@ module ModelGeneratorCommonInput implements ModelGeneratorCommonInputSig<Cpp::Lo
predicate isRelevantType(Type t) { any() } predicate isRelevantType(Type t) { any() }
Type getUnderlyingContentType(DataFlow::ContentSet c) { 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() result = c.(DataFlow::UnionContent).getUnion().getUnspecifiedType()
} }
@@ -340,12 +340,7 @@ private module SummaryModelGeneratorInput implements SummaryModelGeneratorInputS
) )
} }
predicate isField(DataFlow::ContentSet cs) { predicate isField(DataFlow::ContentSet cs) { cs.isSingleton(any(DataFlow::FieldContent fc)) }
exists(DataFlow::Content c | cs.isSingleton(c) |
c instanceof DataFlow::FieldContent or
c instanceof DataFlow::UnionContent
)
}
predicate isCallback(DataFlow::ContentSet c) { none() } predicate isCallback(DataFlow::ContentSet c) { none() }

View File

@@ -1,10 +1,16 @@
| sizeof.cpp:19:15:19:25 | sizeof(int) | 4 | SizeofTypeOperator.getTypeOperand() | file://:0:0:0:0 | int | | 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 | SizeofTypeOperator.getTypeOperand() | file://:0:0:0:0 | char | | 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 | SizeofTypeOperator.getTypeOperand() | file://:0:0:0:0 | int * | | 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 | SizeofTypeOperator.getTypeOperand() | sizeof.cpp:4:7:4:13 | MyClass | | 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 | 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 | 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 | 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 | 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 | 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 | 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 |

View File

@@ -2,8 +2,8 @@ import cpp
from SizeofOperator sto, string elemDesc, Element e from SizeofOperator sto, string elemDesc, Element e
where where
elemDesc = "SizeofTypeOperator.getTypeOperand()" and elemDesc = "SizeofOperator.getTypeOperand()" and
e = sto.(SizeofTypeOperator).getTypeOperand() e = sto.getTypeOperand()
or or
elemDesc = "SizeofExprOperator.getExprOperand()" and elemDesc = "SizeofExprOperator.getExprOperand()" and
e = sto.(SizeofExprOperator).getExprOperand() e = sto.(SizeofExprOperator).getExprOperand()

View File

@@ -170,7 +170,8 @@ namespace Semmle.Extraction.CSharp.Standalone
fileLogger.LogError($" Unhandled exception: {ex}"); 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; return ExitCode.Ok;
} }

View File

@@ -360,5 +360,22 @@ namespace Semmle.Extraction.CSharp
return versionString.InformationalVersion; 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()
: [];
} }
} }

View File

@@ -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) public void Initialize(string outputPath, IEnumerable<(string, string)> compilationInfos, CSharpCompilation compilationIn, CommonOptions options)
{ {
compilation = compilationIn; compilation = compilationIn;
@@ -20,6 +28,7 @@ namespace Semmle.Extraction.CSharp
this.options = options; this.options = options;
LogExtractorInfo(); LogExtractorInfo();
SetReferencePaths(); SetReferencePaths();
LogDiagnostics();
} }
} }
} }

View File

@@ -136,11 +136,7 @@ namespace Semmle.Extraction.CSharp
private int LogDiagnostics() private int LogDiagnostics()
{ {
var filteredDiagnostics = compilation! var filteredDiagnostics = GetFilteredDiagnostics();
.GetDiagnostics()
.Where(e => e.Severity >= DiagnosticSeverity.Error && !errorsToIgnore.Contains(e.Id))
.ToList();
foreach (var error in filteredDiagnostics) foreach (var error in filteredDiagnostics)
{ {
Logger.LogError($" Compilation error: {error}"); Logger.LogError($" Compilation error: {error}");
@@ -148,7 +144,7 @@ namespace Semmle.Extraction.CSharp
if (filteredDiagnostics.Count != 0) if (filteredDiagnostics.Count != 0)
{ {
foreach (var reference in compilation.References) foreach (var reference in compilation!.References)
{ {
Logger.LogInfo($" Resolved reference {reference.Display}"); Logger.LogInfo($" Resolved reference {reference.Display}");
} }
@@ -156,12 +152,5 @@ namespace Semmle.Extraction.CSharp
return filteredDiagnostics.Count; 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
};
} }
} }

View File

@@ -1,3 +1,7 @@
## 1.7.52
No user-facing changes.
## 1.7.51 ## 1.7.51
No user-facing changes. No user-facing changes.

View File

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

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 1.7.51 lastReleaseVersion: 1.7.52

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all name: codeql/csharp-solorigate-all
version: 1.7.52-dev version: 1.7.53-dev
groups: groups:
- csharp - csharp
- solorigate - solorigate

View File

@@ -1,3 +1,7 @@
## 1.7.52
No user-facing changes.
## 1.7.51 ## 1.7.51
No user-facing changes. No user-facing changes.

View File

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

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 1.7.51 lastReleaseVersion: 1.7.52

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries name: codeql/csharp-solorigate-queries
version: 1.7.52-dev version: 1.7.53-dev
groups: groups:
- csharp - csharp
- solorigate - solorigate

View File

@@ -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 ## 5.3.0
### Deprecated APIs ### Deprecated APIs

View File

@@ -1,4 +0,0 @@
---
category: deprecated
---
* `ControlFlowElement.controlsBlock` has been deprecated in favor of the Guards library.

View File

@@ -1,5 +0,0 @@
---
category: feature
---
* Initial support for incremental C# databases via `codeql database create --overlay-base`/`--overlay-changes`.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Updated *roslyn* and *binlog* dependencies in the extractor, which may improve database and analysis quality.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Compilation errors are now included in the debug log when using build-mode none.

View File

@@ -6,7 +6,7 @@
### Major Analysis Improvements ### 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 ### Minor Analysis Improvements

View 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.

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 5.3.0 lastReleaseVersion: 5.4.0

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-all name: codeql/csharp-all
version: 5.3.1-dev version: 5.4.1-dev
groups: csharp groups: csharp
dbscheme: semmlecode.csharp.dbscheme dbscheme: semmlecode.csharp.dbscheme
extractor: csharp extractor: csharp

View File

@@ -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 ## 1.4.3
### Minor Analysis Improvements ### Minor Analysis Improvements

View File

@@ -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.

View File

@@ -2,4 +2,4 @@
### Minor Analysis Improvements ### 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.

View File

@@ -1,4 +1,9 @@
--- ## 1.5.0
category: minorAnalysis
--- ### 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. * 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.

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 1.4.3 lastReleaseVersion: 1.5.0

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries name: codeql/csharp-queries
version: 1.4.4-dev version: 1.5.1-dev
groups: groups:
- csharp - csharp
- queries - queries

View File

@@ -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.

View File

@@ -11,6 +11,7 @@ A list of queries for each suite and language `is available here <https://docs.g
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
codeql-cli-2.23.5
codeql-cli-2.23.3 codeql-cli-2.23.3
codeql-cli-2.23.2 codeql-cli-2.23.2
codeql-cli-2.23.1 codeql-cli-2.23.1

View File

@@ -1,2 +1,3 @@
- `CodeQL queries for Rust <https://github.com/github/codeql/tree/main/rust/ql/src>`__ - `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/>`__ - `CodeQL library reference for Rust <https://codeql.github.com/codeql-standard-libraries/rust/>`__

View File

@@ -1,3 +1,7 @@
## 1.0.35
No user-facing changes.
## 1.0.34 ## 1.0.34
No user-facing changes. No user-facing changes.

View File

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

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 1.0.34 lastReleaseVersion: 1.0.35

View File

@@ -1,5 +1,5 @@
name: codeql-go-consistency-queries name: codeql-go-consistency-queries
version: 1.0.35-dev version: 1.0.36-dev
groups: groups:
- go - go
- queries - queries

View File

@@ -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-312/CleartextLogging.ql
ql/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.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-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/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-338/InsecureRandomness.ql
ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql
ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql

View File

@@ -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-312/CleartextLogging.ql
ql/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.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-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/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-338/InsecureRandomness.ql
ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql
ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql

View File

@@ -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-312/CleartextLogging.ql
ql/go/ql/src/Security/CWE-322/InsecureHostKeyCallback.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-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/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-338/InsecureRandomness.ql
ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql ql/go/ql/src/Security/CWE-347/MissingJwtSignatureCheck.ql
ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql ql/go/ql/src/Security/CWE-352/ConstantOauth2State.ql

View File

@@ -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-285/PamAuthBypass.ql
ql/go/ql/src/experimental/CWE-287/ImproperLdapAuth.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-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-369/DivideByZero.ql
ql/go/ql/src/experimental/CWE-400/DatabaseCallInLoop.ql ql/go/ql/src/experimental/CWE-400/DatabaseCallInLoop.ql
ql/go/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql ql/go/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql

View File

@@ -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 ## 5.0.1
No user-facing changes. No user-facing changes.

View File

@@ -1,6 +1,7 @@
--- ## 5.0.2
category: fix
--- ### Bug Fixes
* Some fixes relating to use of path transformers when extracting a database: * 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. * 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`. * The environment variable `CODEQL_PATH_TRANSFORMER` is now recognized, in addition to `SEMMLE_PATH_TRANSFORMER`.

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 5.0.1 lastReleaseVersion: 5.0.2

View File

@@ -33,6 +33,7 @@ import semmle.go.frameworks.AwsLambda
import semmle.go.frameworks.Beego import semmle.go.frameworks.Beego
import semmle.go.frameworks.BeegoOrm import semmle.go.frameworks.BeegoOrm
import semmle.go.frameworks.Bun import semmle.go.frameworks.Bun
import semmle.go.frameworks.CryptoLibraries
import semmle.go.frameworks.RsCors import semmle.go.frameworks.RsCors
import semmle.go.frameworks.Couchbase import semmle.go.frameworks.Couchbase
import semmle.go.frameworks.Echo import semmle.go.frameworks.Echo

View File

@@ -1,11 +1,12 @@
name: codeql/go-all name: codeql/go-all
version: 5.0.2-dev version: 5.0.3-dev
groups: go groups: go
dbscheme: go.dbscheme dbscheme: go.dbscheme
extractor: go extractor: go
library: true library: true
upgrades: upgrades upgrades: upgrades
dependencies: dependencies:
codeql/concepts: ${workspace}
codeql/dataflow: ${workspace} codeql/dataflow: ${workspace}
codeql/mad: ${workspace} codeql/mad: ${workspace}
codeql/threat-models: ${workspace} codeql/threat-models: ${workspace}

View File

@@ -8,6 +8,10 @@ import go
import semmle.go.dataflow.FunctionInputsAndOutputs import semmle.go.dataflow.FunctionInputsAndOutputs
import semmle.go.concepts.HTTP import semmle.go.concepts.HTTP
import semmle.go.concepts.GeneratedFile 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, * A data-flow node that executes an operating system command,
@@ -505,3 +509,98 @@ module UnmarshalingFunction {
abstract string getFormat(); 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)
}
}
}

View 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") }
}
}
}

View File

@@ -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
}
}
}

View File

@@ -1,3 +1,7 @@
## 1.4.9
No user-facing changes.
## 1.4.8 ## 1.4.8
No user-facing changes. No user-facing changes.

View File

@@ -28,10 +28,10 @@
<example> <example>
<p> <p>
The following code uses the different packages to encrypt/hash The following code uses the different packages to encrypt
some secret data. The first few examples uses DES, MD5, RC4, and SHA1, some secret data. The first example uses DES,
which are older algorithms that are now considered weak. The following which is an older algorithm that is now considered weak. The following
examples use AES and SHA256, which are stronger, more modern algorithms. example uses AES, which is a stronger, more modern algorithm.
</p> </p>
<sample src="examples/Crypto.go" /> <sample src="examples/Crypto.go" />

View 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

View 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>

View 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 + ")"

View 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
}

View 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
}

View 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)
}

View File

@@ -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.

View File

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

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 1.4.8 lastReleaseVersion: 1.4.9

View File

@@ -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())
}
}

View File

@@ -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"

View File

@@ -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>;
}

View File

@@ -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)
}

View File

@@ -1,5 +1,5 @@
name: codeql/go-queries name: codeql/go-queries
version: 1.4.9-dev version: 1.4.10-dev
groups: groups:
- go - go
- queries - queries

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