Compare commits

..

1 Commits

Author SHA1 Message Date
Paolo Tranquilli
f72ed627e1 Javascript: tentative fix to a weird build problem 2025-06-27 16:41:38 +02:00
1285 changed files with 98492 additions and 206156 deletions

View File

@@ -1,11 +0,0 @@
When reviewing code:
* do not review changes in files with `.expected` extension (they are automatically ensured to be correct).
* in `.ql` and `.qll` files, do not try to review the code itself as you don't understand the programming language
well enough to make comments in these languages. You can still check for typos or comment improvements.
When editing `.ql` and `.qll` files:
* All edited `.ql` and `.qll` files should be autoformatted afterwards using the CodeQL CLI.
* To install and use the CodeQL CLI autoformatter:
1. Download and extract CodeQL CLI: `cd /tmp && curl -L -o codeql-linux64.zip https://github.com/github/codeql-cli-binaries/releases/latest/download/codeql-linux64.zip && unzip -q codeql-linux64.zip`
2. Add to PATH: `export PATH="/tmp/codeql:$PATH"`
3. Run autoformatter: `codeql query format [file] --in-place`

View File

@@ -1,23 +0,0 @@
name: Check overlay annotations
on:
push:
branches:
- main
- 'rc/*'
pull_request:
branches:
- main
- 'rc/*'
permissions:
contents: read
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check overlay annotations
run: python config/add-overlay-annotations.py --check java

35
.github/workflows/go-tests-other-os.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: "Go: Run Tests - Other OS"
on:
pull_request:
paths:
- "go/**"
- "!go/documentation/**"
- "!go/ql/**" # don't run other-os if only ql/ files changed
- .github/workflows/go-tests-other-os.yml
- .github/actions/**
- codeql-workspace.yml
- MODULE.bazel
- .bazelrc
- misc/bazel/**
permissions:
contents: read
jobs:
test-mac:
name: Test MacOS
runs-on: macos-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Run tests
uses: ./go/actions/test
test-win:
name: Test Windows
runs-on: windows-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Run tests
uses: ./go/actions/test

22
.github/workflows/go-tests-rtjo.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: "Go: Run RTJO Tests"
on:
pull_request:
types:
- labeled
permissions:
contents: read
jobs:
test-linux:
if: "github.repository_owner == 'github' && github.event.label.name == 'Run: RTJO Language Tests'"
name: RTJO Test Linux (Ubuntu)
runs-on: ubuntu-latest-xl
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Run tests
uses: ./go/actions/test
with:
run-code-checks: true
dynamic-join-order-mode: all

View File

@@ -1,6 +1,6 @@
name: "Go: Run Tests"
on:
pull_request:
push:
paths:
- "go/**"
- "!go/documentation/**"
@@ -8,6 +8,17 @@ on:
- .github/workflows/go-tests.yml
- .github/actions/**
- codeql-workspace.yml
branches:
- main
- "rc/*"
pull_request:
paths:
- "go/**"
- "!go/documentation/**"
- "shared/**"
- .github/workflows/go-tests.yml
- .github/actions/**
- codeql-workspace.yml
- MODULE.bazel
- .bazelrc
- misc/bazel/**

View File

@@ -37,7 +37,6 @@ bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True
# the versions there are canonical, the versions here are used for CI in github/codeql, as well as for the vendoring of dependencies.
RUST_EDITION = "2024"
# run buildutils-internal/scripts/fill-rust-sha256s.py when updating (internal repo)
RUST_VERSION = "1.86.0"
rust = use_extension("@rules_rust//rust:extensions.bzl", "rust")
@@ -48,29 +47,6 @@ rust.toolchain(
"x86_64-apple-darwin",
"aarch64-apple-darwin",
],
# generated by buildutils-internal/scripts/fill-rust-sha256s.py (internal repo)
sha256s = {
"rustc-1.86.0-x86_64-unknown-linux-gnu.tar.xz": "4438b809ce4a083af31ed17aeeedcc8fc60ccffc0625bef1926620751b6989d7",
"rustc-1.86.0-x86_64-apple-darwin.tar.xz": "42b76253626febb7912541a30d3379f463dec89581aad4cb72c6c04fb5a71dc5",
"rustc-1.86.0-aarch64-apple-darwin.tar.xz": "23b8f52102249a47ab5bc859d54c9a3cb588a3259ba3f00f557d50edeca4fde9",
"rustc-1.86.0-x86_64-pc-windows-msvc.tar.xz": "fdde839fea274529a31e51eb85c6df1782cc8479c9d1bc24e2914d66a0de41ab",
"clippy-1.86.0-x86_64-unknown-linux-gnu.tar.xz": "02aaff2c1407d2da8dba19aa4970dd873e311902b120a66cbcdbe51eb8836edf",
"clippy-1.86.0-x86_64-apple-darwin.tar.xz": "bb85efda7bbffaf124867f5ca36d50932b1e8f533c62ee923438afb32ff8fe9a",
"clippy-1.86.0-aarch64-apple-darwin.tar.xz": "239fa3a604b124f0312f2af08537874a1227dba63385484b468cca62e7c4f2f2",
"clippy-1.86.0-x86_64-pc-windows-msvc.tar.xz": "d00498f47d49219f032e2c5eeebdfc3d32317c0dc3d3fd7125327445bc482cb4",
"cargo-1.86.0-x86_64-unknown-linux-gnu.tar.xz": "c5c1590f7e9246ad9f4f97cfe26ffa92707b52a769726596a9ef81565ebd908b",
"cargo-1.86.0-x86_64-apple-darwin.tar.xz": "af163eb02d1a178044d1b4f2375960efd47130f795f6e33d09e345454bb26f4e",
"cargo-1.86.0-aarch64-apple-darwin.tar.xz": "3cb13873d48c3e1e4cc684d42c245226a11fba52af6b047c3346ed654e7a05c0",
"cargo-1.86.0-x86_64-pc-windows-msvc.tar.xz": "e57a9d89619b5604899bac443e68927bdd371e40f2e03e18950b6ceb3eb67966",
"llvm-tools-1.86.0-x86_64-unknown-linux-gnu.tar.xz": "282145ab7a63c98b625856f44b905b4dc726b497246b824632a5790debe95a78",
"llvm-tools-1.86.0-x86_64-apple-darwin.tar.xz": "b55706e92f7da989207c50c13c7add483a9fedd233bc431b106eca2a8f151ec9",
"llvm-tools-1.86.0-aarch64-apple-darwin.tar.xz": "04d3618c686845853585f036e3211eb9e18f2d290f4610a7a78bdc1fcce1ebd9",
"llvm-tools-1.86.0-x86_64-pc-windows-msvc.tar.xz": "721a17cc8dc219177e4277a3592253934ef08daa1e1b12eda669a67d15fad8dd",
"rust-std-1.86.0-x86_64-unknown-linux-gnu.tar.xz": "67be7184ea388d8ce0feaf7fdea46f1775cfc2970930264343b3089898501d37",
"rust-std-1.86.0-x86_64-apple-darwin.tar.xz": "3b1140d54870a080080e84700143f4a342fbd02a410a319b05d9c02e7dcf44cc",
"rust-std-1.86.0-aarch64-apple-darwin.tar.xz": "0fb121fb3b8fa9027d79ff598500a7e5cd086ddbc3557482ed3fdda00832c61b",
"rust-std-1.86.0-x86_64-pc-windows-msvc.tar.xz": "3d5354b7b9cb950b58bff3fce18a652aa374bb30c8f70caebd3bd0b43cb41a33",
},
versions = [RUST_VERSION],
)
use_repo(rust, "rust_toolchains")

View File

@@ -1,9 +1,3 @@
## 0.4.13
### Bug Fixes
* The `actions/artifact-poisoning/critical` and `actions/artifact-poisoning/medium` queries now exclude artifacts downloaded to `$[{ runner.temp }}` in addition to `/tmp`.
## 0.4.12
### Minor Analysis Improvements

View File

@@ -1,5 +0,0 @@
## 0.4.13
### Bug Fixes
* The `actions/artifact-poisoning/critical` and `actions/artifact-poisoning/medium` queries now exclude artifacts downloaded to `$[{ runner.temp }}` in addition to `/tmp`.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.4.13
lastReleaseVersion: 0.4.12

View File

@@ -72,7 +72,7 @@ string normalizePath(string path) {
then result = path
else
// foo -> GITHUB_WORKSPACE/foo
if path.regexpMatch("^[^$/~].*")
if path.regexpMatch("^[^/~].*")
then result = "GITHUB_WORKSPACE/" + path.regexpReplaceAll("/$", "")
else
// ~/foo -> ~/foo

View File

@@ -262,10 +262,8 @@ class ArtifactPoisoningSink extends DataFlow::Node {
ArtifactPoisoningSink() {
download.getAFollowingStep() = poisonable and
// excluding artifacts downloaded to the temporary directory
// excluding artifacts downloaded to /tmp
not download.getPath().regexpMatch("^/tmp.*") and
not download.getPath().regexpMatch("^\\$\\{\\{\\s*runner\\.temp\\s*}}.*") and
not download.getPath().regexpMatch("^\\$RUNNER_TEMP.*") and
(
poisonable.(Run).getScript() = this.asExpr() and
(

View File

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

View File

@@ -1,7 +1,3 @@
## 0.6.5
No user-facing changes.
## 0.6.4
No user-facing changes.

View File

@@ -1,4 +1,6 @@
## Overview
# Environment Path Injection
## Description
GitHub Actions allow to define the system PATH variable by writing to a file pointed to by the `GITHUB_PATH` environment variable. Writing to this file appends a directory to the system PATH variable and automatically makes it available to all subsequent actions in the current job.
@@ -10,11 +12,11 @@ echo "$HOME/.local/bin" >> $GITHUB_PATH
If an attacker can control the contents of the system PATH, they are able to influence what commands are run in subsequent steps of the same job.
## Recommendation
## Recommendations
Do not allow untrusted data to influence the system PATH: Avoid using untrusted data sources (e.g., artifact content) to define the system PATH.
## Example
## Examples
### Incorrect Usage
@@ -34,4 +36,4 @@ If an attacker can manipulate the value being set, such as through artifact down
## References
- GitHub Docs: [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions).
- [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions)

View File

@@ -1,4 +1,6 @@
## Overview
# Environment Path Injection
## Description
GitHub Actions allow to define the system PATH variable by writing to a file pointed to by the `GITHUB_PATH` environment variable. Writing to this file appends a directory to the system PATH variable and automatically makes it available to all subsequent actions in the current job.
@@ -10,11 +12,11 @@ echo "$HOME/.local/bin" >> $GITHUB_PATH
If an attacker can control the contents of the system PATH, they are able to influence what commands are run in subsequent steps of the same job.
## Recommendation
## Recommendations
Do not allow untrusted data to influence the system PATH: Avoid using untrusted data sources (e.g., artifact content) to define the system PATH.
## Example
## Examples
### Incorrect Usage
@@ -34,4 +36,4 @@ If an attacker can manipulate the value being set, such as through artifact down
## References
- GitHub Docs: [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions).
- [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions)

View File

@@ -1,4 +1,6 @@
## Overview
# Environment Variable Injection
## Description
GitHub Actions allow to define environment variables by writing to a file pointed to by the `GITHUB_ENV` environment variable:
@@ -35,7 +37,7 @@ steps:
If an attacker can control the values assigned to environment variables and there is no sanitization in place, the attacker will be able to inject additional variables by injecting new lines or `{delimiters}`.
## Recommendation
## Recommendations
1. **Do not allow untrusted data to influence environment variables**:
@@ -62,7 +64,7 @@ If an attacker can control the values assigned to environment variables and ther
} >> "$GITHUB_ENV"
```
## Example
## Examples
### Example of Vulnerability
@@ -111,5 +113,5 @@ An attacker is be able to run arbitrary code by injecting environment variables
## References
- GitHub Docs: [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions).
- Synacktiv: [GitHub Actions Exploitation: Repo Jacking and Environment Manipulation](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation).
- [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions)
- [GitHub Actions Exploitation: Repo Jacking and Environment Manipulation](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation)

View File

@@ -1,4 +1,6 @@
## Overview
# Environment Variable Injection
## Description
GitHub Actions allow to define environment variables by writing to a file pointed to by the `GITHUB_ENV` environment variable:
@@ -35,7 +37,7 @@ steps:
If an attacker can control the values assigned to environment variables and there is no sanitization in place, the attacker will be able to inject additional variables by injecting new lines or `{delimiters}`.
## Recommendation
## Recommendations
1. **Do not allow untrusted data to influence environment variables**:
@@ -62,7 +64,7 @@ If an attacker can control the values assigned to environment variables and ther
} >> "$GITHUB_ENV"
```
## Example
## Examples
### Example of Vulnerability
@@ -111,5 +113,5 @@ An attacker would be able to run arbitrary code by injecting environment variabl
## References
- GitHub Docs: [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions).
- Synacktiv: [GitHub Actions Exploitation: Repo Jacking and Environment Manipulation](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation).
- [Workflow commands for GitHub Actions](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions)
- [GitHub Actions Exploitation: Repo Jacking and Environment Manipulation](https://www.synacktiv.com/publications/github-actions-exploitation-repo-jacking-and-environment-manipulation)

View File

@@ -1,16 +1,18 @@
## Overview
# Code Injection in GitHub Actions
## Description
Using user-controlled input in GitHub Actions may lead to code injection in contexts like _run:_ or _script:_.
Code injection in GitHub Actions may allow an attacker to exfiltrate any secrets used in the workflow and the temporary GitHub repository authorization token. The token may have write access to the repository, allowing an attacker to make changes to the repository.
## Recommendation
## Recommendations
The best practice to avoid code injection vulnerabilities in GitHub workflows is to set the untrusted input value of the expression to an intermediate environment variable and then use the environment variable using the native syntax of the shell/script interpreter (that is, not _${{ env.VAR }}_).
It is also recommended to limit the permissions of any tokens used by a workflow such as the GITHUB_TOKEN.
## Example
## Examples
### Incorrect Usage

View File

@@ -1,16 +1,18 @@
## Overview
# Code Injection in GitHub Actions
## Description
Using user-controlled input in GitHub Actions may lead to code injection in contexts like _run:_ or _script:_.
Code injection in GitHub Actions may allow an attacker to exfiltrate any secrets used in the workflow and the temporary GitHub repository authorization token. The token may have write access to the repository, allowing an attacker to make changes to the repository.
## Recommendation
## Recommendations
The best practice to avoid code injection vulnerabilities in GitHub workflows is to set the untrusted input value of the expression to an intermediate environment variable and then use the environment variable using the native syntax of the shell/script interpreter (that is, not _${{ env.VAR }}_).
It is also recommended to limit the permissions of any tokens used by a workflow such as the GITHUB_TOKEN.
## Example
## Examples
### Incorrect Usage

View File

@@ -1,11 +1,13 @@
## Overview
# Use of Actions with known vulnerabilities
## Description
The security of the workflow and the repository could be compromised by GitHub Actions workflows that utilize GitHub Actions with known vulnerabilities.
## Recommendation
## Recommendations
Either remove the component from the workflow or upgrade it to a version that is not vulnerable.
## References
- GitHub Docs: [Keeping your actions up to date with Dependabot](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot).
- [GitHub Docs: Keeping your actions up to date with Dependabot](https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot)

View File

@@ -1,21 +1,12 @@
## Overview
# Actions Job and Workflow Permissions are not set
## Description
If a GitHub Actions job or workflow has no explicit permissions set, then the repository permissions are used. Repositories created under organizations inherit the organization permissions. The organizations or repositories created before February 2023 have the default permissions set to read-write. Often these permissions do not adhere to the principle of least privilege and can be reduced to read-only, leaving the `write` permission only to a specific types as `issues: write` or `pull-requests: write`.
## Recommendation
## Recommendations
Add the `permissions` key to the job or the root of workflow (in this case it is applied to all jobs in the workflow that do not have their own `permissions` key) and assign the least privileges required to complete the task.
## Example
### Incorrect Usage
```yaml
name: "My workflow"
# No permissions block
```
### Correct Usage
Add the `permissions` key to the job or the root of workflow (in this case it is applied to all jobs in the workflow that do not have their own `permissions` key) and assign the least privileges required to complete the task:
```yaml
name: "My workflow"
@@ -36,4 +27,4 @@ jobs:
## References
- GitHub Docs: [Assigning permissions to jobs](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/assigning-permissions-to-jobs).
- [Assigning permissions to jobs](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/assigning-permissions-to-jobs)

View File

@@ -1,12 +1,14 @@
## Overview
# Improper Access Control
## Description
Sometimes labels are used to approve GitHub Actions. An authorization check may not be properly implemented, allowing an attacker to mutate the code after it has been reviewed and approved by label.
## Recommendation
## Recommendations
When using labels, make sure that the code cannot be modified after it has been reviewed and the label has been set.
## Example
## Examples
### Incorrect Usage
@@ -55,4 +57,4 @@ jobs:
## References
- GitHub Docs: [Events that trigger workflows](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target).
- [Events that trigger workflows](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target)

View File

@@ -1,12 +1,14 @@
## Overview
# Excessive Secrets Exposure
## Description
When the workflow runner cannot determine what secrets are needed to run the workflow, it will pass all the available secrets to the runner including organization and repository secrets. This violates the least privileged principle and increases the impact of a potential vulnerability affecting the workflow.
## Recommendation
## Recommendations
Only pass those secrets that are needed by the workflow. Avoid using expressions such as `toJSON(secrets)` or dynamically accessed secrets such as `secrets[format('GH_PAT_%s', matrix.env)]` since the workflow will need to receive all secrets to decide at runtime which one needs to be used.
## Example
## Examples
### Incorrect Usage
@@ -46,5 +48,5 @@ env:
## References
- GitHub Docs: [Using secrets in GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#using-encrypted-secrets-in-a-workflow).
- poutine: [Job uses all secrets](https://github.com/boostsecurityio/poutine/blob/main/docs/content/en/rules/job_all_secrets.md).
- [Using secrets in GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#using-encrypted-secrets-in-a-workflow)
- [Job uses all secrets](https://github.com/boostsecurityio/poutine/blob/main/docs/content/en/rules/job_all_secrets.md)

View File

@@ -1,4 +1,6 @@
## Overview
# Storage of sensitive information in GitHub Actions artifact
## Description
Sensitive information included in a GitHub Actions artifact can allow an attacker to access the sensitive information if the artifact is published.
@@ -8,8 +10,6 @@ Only store information that is meant to be publicly available in a GitHub Action
## Example
### Incorrect Usage
The following example uses `actions/checkout` to checkout code which stores the GITHUB_TOKEN in the \`.git/config\` file and then stores the contents of the \`.git\` repository into the artifact:
```yaml
@@ -28,8 +28,6 @@ jobs:
path: .
```
### Correct Usage
The issue has been fixed below, where the `actions/upload-artifact` uses a version (v4+) which does not include hidden files or directories into the artifact.
```yaml

View File

@@ -1,12 +1,14 @@
## Overview
# Unmasked Secret Exposure
## Description
Secrets derived from other secrets are not known to the workflow runner, and therefore are not masked unless explicitly registered.
## Recommendation
## Recommendations
Avoid defining non-plain secrets. For example, do not define a new secret containing a JSON object and then read properties out of it from the workflow, since these read values will not be masked by the workflow runner.
## Example
## Examples
### Incorrect Usage
@@ -32,4 +34,4 @@ Avoid defining non-plain secrets. For example, do not define a new secret contai
## References
- GitHub Docs: [Using secrets in GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#using-encrypted-secrets-in-a-workflow).
- [Using secrets in GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#using-encrypted-secrets-in-a-workflow)

View File

@@ -1,4 +1,6 @@
## Overview
# Cache Poisoning in GitHub Actions
## Description
GitHub Actions cache poisoning is a technique that allows an attacker to inject malicious content into the Action's cache from unprivileged workflow, potentially leading to code execution in privileged workflows.
@@ -21,7 +23,7 @@ In GitHub Actions, cache scopes are primarily determined by the branch structure
Due to the above design, if something is cached in the context of the default branch (e.g., `main`), it becomes accessible to any feature branch derived from `main`.
## Recommendation
## Recommendations
1. Avoid using caching in workflows that handle sensitive operations like releases.
2. If caching must be used:
@@ -32,7 +34,7 @@ Due to the above design, if something is cached in the context of the default br
4. Never run untrusted code in the context of the default branch.
5. Sign the cache value cryptographically and verify the signature before usage.
## Example
## Examples
### Incorrect Usage
@@ -76,6 +78,6 @@ jobs:
## References
- Adnan Khan's Blog: [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/).
- GitHub Docs: [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows).
- Scribe Security Blog: [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/).
- [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/)
- [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows)
- [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/)

View File

@@ -1,4 +1,6 @@
## Overview
# Cache Poisoning in GitHub Actions
## Description
GitHub Actions cache poisoning is a technique that allows an attacker to inject malicious content into the Action's cache from unprivileged workflow, potentially leading to code execution in privileged workflows.
@@ -21,7 +23,7 @@ In GitHub Actions, cache scopes are primarily determined by the branch structure
Due to the above design, if something is cached in the context of the default branch (e.g., `main`), it becomes accessible to any feature branch derived from `main`.
## Recommendation
## Recommendations
1. Avoid using caching in workflows that handle sensitive operations like releases.
2. If caching must be used:
@@ -32,7 +34,7 @@ Due to the above design, if something is cached in the context of the default br
4. Never run untrusted code in the context of the default branch.
5. Sign the cache value cryptographically and verify the signature before usage.
## Example
## Examples
### Incorrect Usage
@@ -121,6 +123,6 @@ jobs:
## References
- Adnan Khan's Blog: [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/).
- GitHub Docs: [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows).
- Scribe Security Blog: [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/).
- [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/)
- [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows)
- [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/)

View File

@@ -1,4 +1,6 @@
## Overview
# Cache Poisoning in GitHub Actions
## Description
GitHub Actions cache poisoning is a technique that allows an attacker to inject malicious content into the Action's cache from unprivileged workflow, potentially leading to code execution in privileged workflows.
@@ -21,7 +23,7 @@ In GitHub Actions, cache scopes are primarily determined by the branch structure
Due to the above design, if something is cached in the context of the default branch (e.g., `main`), it becomes accessible to any feature branch derived from `main`.
## Recommendation
## Recommendations
1. Avoid using caching in workflows that handle sensitive operations like releases.
2. If caching must be used:
@@ -32,7 +34,7 @@ Due to the above design, if something is cached in the context of the default br
4. Never run untrusted code in the context of the default branch.
5. Sign the cache value cryptographically and verify the signature before usage.
## Example
## Examples
### Incorrect Usage
@@ -78,6 +80,6 @@ jobs:
## References
- Adnan Khan's Blog: [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/).
- GitHub Docs: [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows).
- Scribe Security Blog: [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/).
- [The Monsters in Your Build Cache GitHub Actions Cache Poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/)
- [GitHub Actions Caching Documentation](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows)
- [Cache Poisoning in GitHub Actions](https://scribesecurity.com/blog/github-cache-poisoning/)

View File

@@ -1,15 +1,17 @@
## Overview
# Untrusted Checkout TOCTOU (Time-of-check to time-of-use)
## Description
Untrusted Checkout is protected by a security check but the checked-out branch can be changed after the check.
## Recommendation
## Recommendations
Verify that the code has not been modified after the security check. This may be achieved differently depending on the type of check:
- Deployment Environment Approval: Make sure to use a non-mutable reference to the code to be executed. For example use a `sha` instead of a `ref`.
- Label Gates: Make sure to use a non-mutable reference to the code to be executed. For example use a `sha` instead of a `ref`.
## Example
## Examples
### Incorrect Usage (Deployment Environment Approval)
@@ -97,4 +99,4 @@ jobs:
## References
- [ActionsTOCTOU](https://github.com/AdnaneKhan/ActionsTOCTOU).
- [ActionsTOCTOU](https://github.com/AdnaneKhan/ActionsTOCTOU)

View File

@@ -1,15 +1,17 @@
## Overview
# Untrusted Checkout TOCTOU (Time-of-check to time-of-use)
## Description
Untrusted Checkout is protected by a security check but the checked-out branch can be changed after the check.
## Recommendation
## Recommendations
Verify that the code has not been modified after the security check. This may be achieved differently depending on the type of check:
- Deployment Environment Approval: Make sure to use a non-mutable reference to the code to be executed. For example use a `sha` instead of a `ref`.
- Label Gates: Make sure to use a non-mutable reference to the code to be executed. For example use a `sha` instead of a `ref`.
## Example
## Examples
### Incorrect Usage (Deployment Environment Approval)
@@ -97,4 +99,4 @@ jobs:
## References
- [ActionsTOCTOU](https://github.com/AdnaneKhan/ActionsTOCTOU).
- [ActionsTOCTOU](https://github.com/AdnaneKhan/ActionsTOCTOU)

View File

@@ -1,4 +1,6 @@
## Overview
# If Condition Always Evaluates to True
## Description
GitHub Workflow Expressions (`${{ ... }}`) used in the `if` condition of jobs or steps must not contain extra characters or spaces. Otherwise, the condition is invariably evaluated to `true`.
@@ -12,7 +14,7 @@ To avoid the vulnerability where an `if` condition always evaluates to `true`, i
2. Avoid multiline or spaced-out conditional expressions that might inadvertently introduce unwanted characters or formatting.
3. Test the workflow to ensure the `if` conditions behave as expected under different scenarios.
## Example
## Examples
### Correct Usage
@@ -58,4 +60,4 @@ To avoid the vulnerability where an `if` condition always evaluates to `true`, i
## References
- GitHub actions/runner Issues: [Expression Always True](https://github.com/actions/runner/issues/1173).
- [Expression Always True Github Issue](https://github.com/actions/runner/issues/1173)

View File

@@ -1,4 +1,6 @@
## Overview
# If Condition Always Evaluates to True
## Description
GitHub Workflow Expressions (`${{ ... }}`) used in the `if` condition of jobs or steps must not contain extra characters or spaces. Otherwise, the condition is invariably evaluated to `true`.
@@ -12,7 +14,7 @@ To avoid the vulnerability where an `if` condition always evaluates to `true`, i
2. Avoid multiline or spaced-out conditional expressions that might inadvertently introduce unwanted characters or formatting.
3. Test the workflow to ensure the `if` conditions behave as expected under different scenarios.
## Example
## Examples
### Correct Usage
@@ -58,4 +60,4 @@ To avoid the vulnerability where an `if` condition always evaluates to `true`, i
## References
- GitHub actions/runner Issues: [Expression Always True](https://github.com/actions/runner/issues/1173).
- [Expression Always True Github Issue](https://github.com/actions/runner/issues/1173)

View File

@@ -1,14 +1,16 @@
## Overview
# Artifact poisoning
## Description
The workflow downloads artifacts that may be poisoned by an attacker in previously triggered workflows. If the contents of these artifacts are not correctly extracted, stored and verified, they may lead to repository compromise if untrusted code gets executed in a privileged job.
## Recommendation
## Recommendations
- Always consider artifacts content as untrusted.
- Extract the contents of artifacts to a temporary folder so they cannot override existing files.
- Verify the contents of the artifacts downloaded. If an artifact is expected to contain a numeric value, verify it before using it.
## Example
## Examples
### Incorrect Usage
@@ -67,4 +69,4 @@ jobs:
## References
- GitHub Security Lab Research: [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/).
- [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)

View File

@@ -1,14 +1,16 @@
## Overview
# Artifact poisoning
## Description
The workflow downloads artifacts that may be poisoned by an attacker in previously triggered workflows. If the contents of these artifacts are not correctly extracted, stored and verified, they may lead to repository compromise if untrusted code gets executed in a privileged job.
## Recommendation
## Recommendations
- Always consider artifacts content as untrusted.
- Extract the contents of artifacts to a temporary folder so they cannot override existing files.
- Verify the contents of the artifacts downloaded. If an artifact is expected to contain a numeric value, verify it before using it.
## Example
## Examples
### Incorrect Usage
@@ -67,4 +69,4 @@ jobs:
## References
- GitHub Security Lab Research: [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/).
- [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)

View File

@@ -1,12 +1,14 @@
## Overview
# Unpinned tag for 3rd party Action in workflow
## Description
Using a tag for a 3rd party Action that is not pinned to a commit can lead to executing an untrusted Action through a supply chain attack.
## Recommendation
## Recommendations
Pinning an action to a full length commit SHA is currently the only way to use a non-immutable action as an immutable release. Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload. When selecting a SHA, you should verify it is from the action's repository and not a repository fork.
## Example
## Examples
### Incorrect Usage
@@ -22,4 +24,4 @@ Pinning an action to a full length commit SHA is currently the only way to use a
## References
- GitHub Docs: [Using third-party actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions).
- [Using third-party actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions)

View File

@@ -1,8 +1,10 @@
## Overview
# Execution of Untrusted Checked-out Code
## Description
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.
## Recommendation
## Recommendations
- Avoid using `pull_request_target` unless necessary.
- Employ unprivileged `pull_request` workflows followed by `workflow_run` for privileged operations.
@@ -12,7 +14,7 @@ The best practice is to handle the potentially untrusted pull request via the **
The artifacts downloaded from the first workflow should be considered untrusted and must be verified.
## Example
## Examples
### Incorrect Usage
@@ -132,4 +134,4 @@ jobs:
## References
- GitHub Security Lab Research: [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/).
- [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)

View File

@@ -1,8 +1,10 @@
## Overview
# Execution of Untrusted Checked-out Code
## Description
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.
## Recommendation
## Recommendations
- Avoid using `pull_request_target` unless necessary.
- Employ unprivileged `pull_request` workflows followed by `workflow_run` for privileged operations.
@@ -12,7 +14,7 @@ The best practice is to handle the potentially untrusted pull request via the **
The artifacts downloaded from the first workflow should be considered untrusted and must be verified.
## Example
## Examples
### Incorrect Usage
@@ -132,4 +134,4 @@ jobs:
## References
- GitHub Security Lab Research: [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/).
- [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)

View File

@@ -1,8 +1,10 @@
## Overview
# Execution of Untrusted Checked-out Code
## Description
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.
## Recommendation
## Recommendations
- Avoid using `pull_request_target` unless necessary.
- Employ unprivileged `pull_request` workflows followed by `workflow_run` for privileged operations.
@@ -12,7 +14,7 @@ The best practice is to handle the potentially untrusted pull request via the **
The artifacts downloaded from the first workflow should be considered untrusted and must be verified.
## Example
## Examples
### Incorrect Usage
@@ -132,4 +134,4 @@ jobs:
## References
- GitHub Security Lab Research: [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/).
- [Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)

View File

@@ -1,11 +1,13 @@
## Overview
# Unneccesary use of advanced configuration
## Description
The CodeQL workflow does not use any custom settings and could be simplified by switching to the CodeQL default setup.
## Recommendation
## Recommendations
If there is no reason to have a custom configuration switch to the CodeQL default setup.
## References
- GitHub Docs: [Configuring Default Setup for a repository](https://docs.github.com/en/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning#configuring-default-setup-for-a-repository).
- [GitHub Docs: Configuring Default Setup for a repository](https://docs.github.com/en/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning#configuring-default-setup-for-a-repository)

View File

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.6.5
lastReleaseVersion: 0.6.4

View File

@@ -1,16 +1,18 @@
## Overview
# Argument Injection in GitHub Actions
## Description
Passing user-controlled arguments to certain commands in the context of `Run` steps may lead to arbitrary code execution.
Argument injection in GitHub Actions may allow an attacker to exfiltrate any secrets used in the workflow and the temporary GitHub repository authorization token. The token may have write access to the repository, allowing the attacker to make changes to the repository.
## Recommendation
## Recommendations
When possible avoid passing user-controlled data to commands which may spawn new processes using some of their arguments.
It is also recommended to limit the permissions of any tokens used by a workflow such as the GITHUB_TOKEN.
## Example
## Examples
### Incorrect Usage
@@ -33,7 +35,7 @@ An attacker may set the body of an Issue comment to `BAR/g;1e whoami;#` and the
## References
- Common Weakness Enumeration: [CWE-88](https://cwe.mitre.org/data/definitions/88.html).
- [Argument Injection Vectors](https://sonarsource.github.io/argument-injection-vectors/).
- Argument Injection Vectors: [Argument Injection Explained](https://sonarsource.github.io/argument-injection-vectors/explained/).
- [GTFOBins](https://gtfobins.github.io/).
- [Common Weakness Enumeration: CWE-88](https://cwe.mitre.org/data/definitions/88.html).
- [Argument Injection Explained](https://sonarsource.github.io/argument-injection-vectors/explained/)
- [Argument Injection Vectors](https://sonarsource.github.io/argument-injection-vectors/)
- [GTFOBins](https://gtfobins.github.io/)

View File

@@ -1,16 +1,18 @@
## Overview
# Argument Injection in GitHub Actions
## Description
Passing user-controlled arguments to certain commands in the context of `Run` steps may lead to arbitrary code execution.
Argument injection in GitHub Actions may allow an attacker to exfiltrate any secrets used in the workflow and the temporary GitHub repository authorization token. The token may have write access to the repository, allowing the attacker to make changes to the repository.
## Recommendation
## Recommendations
When possible avoid passing user-controlled data to commands which may spawn new processes using some of their arguments.
It is also recommended to limit the permissions of any tokens used by a workflow such as the GITHUB_TOKEN.
## Example
## Examples
### Incorrect Usage
@@ -33,7 +35,7 @@ An attacker may set the body of an Issue comment to `BAR|g;1e whoami;#` and the
## References
- Common Weakness Enumeration: [CWE-88](https://cwe.mitre.org/data/definitions/88.html).
- [Argument Injection Vectors](https://sonarsource.github.io/argument-injection-vectors/).
- Argument Injection Vectors: [Argument Injection Explained](https://sonarsource.github.io/argument-injection-vectors/explained/).
- [GTFOBins](https://gtfobins.github.io/).
- [Common Weakness Enumeration: CWE-88](https://cwe.mitre.org/data/definitions/88.html).
- [Argument Injection Explained](https://sonarsource.github.io/argument-injection-vectors/explained/)
- [Argument Injection Vectors](https://sonarsource.github.io/argument-injection-vectors/)
- [GTFOBins](https://gtfobins.github.io/)

View File

@@ -1,12 +1,14 @@
## Overview
# Unversioned Immutable Action
## Description
This action is eligible for Immutable Actions, a new GitHub feature that is currently only available for internal users. Immutable Actions are released as packages in the GitHub package registry instead of resolved from a pinned SHA at the repository. The Immutable Action provides the same immutability as pinning the version to a SHA but with improved readability and additional security guarantees.
## Recommendation
## Recommendations
For internal users: when using [immutable actions](https://github.com/github/package-registry-team/blob/main/docs/immutable-actions/immutable-actions-howto.md) use the full semantic version of the action. This will ensure that the action is resolved to the exact version stored in the GitHub package registry.
## Example
## Examples
### Incorrect Usage
@@ -23,4 +25,4 @@ For internal users: when using [immutable actions](https://github.com/github/pac
## References
- [Consuming immutable actions]().
- [Consuming immutable actions]()

View File

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

View File

@@ -1,19 +0,0 @@
on:
workflow_run:
workflows:
- Benchmark
types:
- completed
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download From PR
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
path: ${{ runner.temp }}/artifacts/
- run: npm install

View File

@@ -1,19 +0,0 @@
on:
workflow_run:
workflows:
- Benchmark
types:
- completed
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download From PR
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
path: /tmp/artifacts/
- run: npm install

View File

@@ -1,19 +0,0 @@
on:
workflow_run:
workflows:
- Benchmark
types:
- completed
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download From PR
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
path: $RUNNER_TEMP/artifacts/
- run: npm install

View File

@@ -1,18 +0,0 @@
on:
workflow_run:
workflows:
- Benchmark
types:
- completed
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download From PR
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
- run: npm install

View File

@@ -1,19 +0,0 @@
on:
workflow_run:
workflows:
- Benchmark
types:
- completed
jobs:
benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download From PR
uses: actions/download-artifact@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
path: ${{ runner.temp }}/artifacts/
- run: npm install

View File

@@ -13,7 +13,6 @@ edges
| .github/workflows/artifactpoisoning42.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | provenance | Config |
| .github/workflows/artifactpoisoning71.yml:9:9:16:6 | Uses Step | .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | provenance | Config |
| .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | provenance | Config |
| .github/workflows/artifactpoisoning96.yml:13:9:18:6 | Uses Step | .github/workflows/artifactpoisoning96.yml:18:14:18:24 | npm install | provenance | Config |
| .github/workflows/artifactpoisoning101.yml:10:9:16:6 | Uses Step | .github/workflows/artifactpoisoning101.yml:17:14:19:59 | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | provenance | Config |
| .github/workflows/test18.yml:12:15:33:12 | Uses Step | .github/workflows/test18.yml:36:15:40:58 | Uses Step | provenance | Config |
| .github/workflows/test25.yml:22:9:32:6 | Uses Step: downloadBuildScan | .github/workflows/test25.yml:39:14:40:45 | ./gradlew buildScanPublishPrevious\n | provenance | Config |
@@ -45,8 +44,6 @@ nodes
| .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | semmle.label | python test.py |
| .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | semmle.label | Uses Step |
| .github/workflows/artifactpoisoning92.yml:29:14:29:26 | make snapshot | semmle.label | make snapshot |
| .github/workflows/artifactpoisoning96.yml:13:9:18:6 | Uses Step | semmle.label | Uses Step |
| .github/workflows/artifactpoisoning96.yml:18:14:18:24 | npm install | semmle.label | npm install |
| .github/workflows/artifactpoisoning101.yml:10:9:16:6 | Uses Step | semmle.label | Uses Step |
| .github/workflows/artifactpoisoning101.yml:17:14:19:59 | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | semmle.label | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n |
| .github/workflows/test18.yml:12:15:33:12 | Uses Step | semmle.label | Uses Step |
@@ -69,7 +66,6 @@ subpaths
| .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | python test.py | .github/workflows/artifactpoisoning81.yml:3:5:3:23 | pull_request_target | pull_request_target |
| .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | Uses Step | .github/workflows/artifactpoisoning92.yml:3:3:3:14 | workflow_run | workflow_run |
| .github/workflows/artifactpoisoning92.yml:29:14:29:26 | make snapshot | .github/actions/download-artifact-2/action.yaml:6:7:25:4 | Uses Step | .github/workflows/artifactpoisoning92.yml:29:14:29:26 | make snapshot | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning92.yml:29:14:29:26 | make snapshot | make snapshot | .github/workflows/artifactpoisoning92.yml:3:3:3:14 | workflow_run | workflow_run |
| .github/workflows/artifactpoisoning96.yml:18:14:18:24 | npm install | .github/workflows/artifactpoisoning96.yml:13:9:18:6 | Uses Step | .github/workflows/artifactpoisoning96.yml:18:14:18:24 | npm install | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning96.yml:18:14:18:24 | npm install | npm install | .github/workflows/artifactpoisoning96.yml:2:3:2:14 | workflow_run | workflow_run |
| .github/workflows/artifactpoisoning101.yml:17:14:19:59 | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | .github/workflows/artifactpoisoning101.yml:10:9:16:6 | Uses Step | .github/workflows/artifactpoisoning101.yml:17:14:19:59 | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/artifactpoisoning101.yml:17:14:19:59 | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | .github/workflows/artifactpoisoning101.yml:4:3:4:21 | pull_request_target | pull_request_target |
| .github/workflows/test18.yml:36:15:40:58 | Uses Step | .github/workflows/test18.yml:12:15:33:12 | Uses Step | .github/workflows/test18.yml:36:15:40:58 | Uses Step | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/test18.yml:36:15:40:58 | Uses Step | Uses Step | .github/workflows/test18.yml:3:5:3:16 | workflow_run | workflow_run |
| .github/workflows/test25.yml:39:14:40:45 | ./gradlew buildScanPublishPrevious\n | .github/workflows/test25.yml:22:9:32:6 | Uses Step: downloadBuildScan | .github/workflows/test25.yml:39:14:40:45 | ./gradlew buildScanPublishPrevious\n | Potential artifact poisoning in $@, which may be controlled by an external user ($@). | .github/workflows/test25.yml:39:14:40:45 | ./gradlew buildScanPublishPrevious\n | ./gradlew buildScanPublishPrevious\n | .github/workflows/test25.yml:2:3:2:14 | workflow_run | workflow_run |

View File

@@ -13,7 +13,6 @@ edges
| .github/workflows/artifactpoisoning42.yml:13:9:21:6 | Run Step | .github/workflows/artifactpoisoning42.yml:22:14:22:18 | ./cmd | provenance | Config |
| .github/workflows/artifactpoisoning71.yml:9:9:16:6 | Uses Step | .github/workflows/artifactpoisoning71.yml:17:14:18:40 | sed -f config foo.md > bar.md\n | provenance | Config |
| .github/workflows/artifactpoisoning81.yml:28:9:31:6 | Uses Step | .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | provenance | Config |
| .github/workflows/artifactpoisoning96.yml:13:9:18:6 | Uses Step | .github/workflows/artifactpoisoning96.yml:18:14:18:24 | npm install | provenance | Config |
| .github/workflows/artifactpoisoning101.yml:10:9:16:6 | Uses Step | .github/workflows/artifactpoisoning101.yml:17:14:19:59 | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | provenance | Config |
| .github/workflows/test18.yml:12:15:33:12 | Uses Step | .github/workflows/test18.yml:36:15:40:58 | Uses Step | provenance | Config |
| .github/workflows/test25.yml:22:9:32:6 | Uses Step: downloadBuildScan | .github/workflows/test25.yml:39:14:40:45 | ./gradlew buildScanPublishPrevious\n | provenance | Config |
@@ -45,8 +44,6 @@ nodes
| .github/workflows/artifactpoisoning81.yml:31:14:31:27 | python test.py | semmle.label | python test.py |
| .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | semmle.label | Uses Step |
| .github/workflows/artifactpoisoning92.yml:29:14:29:26 | make snapshot | semmle.label | make snapshot |
| .github/workflows/artifactpoisoning96.yml:13:9:18:6 | Uses Step | semmle.label | Uses Step |
| .github/workflows/artifactpoisoning96.yml:18:14:18:24 | npm install | semmle.label | npm install |
| .github/workflows/artifactpoisoning101.yml:10:9:16:6 | Uses Step | semmle.label | Uses Step |
| .github/workflows/artifactpoisoning101.yml:17:14:19:59 | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n | semmle.label | PR_NUMBER=$(./get_pull_request_number.sh pr_number.txt)\necho "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT \n |
| .github/workflows/test18.yml:12:15:33:12 | Uses Step | semmle.label | Uses Step |

View File

@@ -51,16 +51,6 @@ edges
| .github/workflows/artifactpoisoning92.yml:19:9:25:6 | Run Step: metadata | .github/workflows/artifactpoisoning92.yml:25:9:28:6 | Uses Step |
| .github/workflows/artifactpoisoning92.yml:25:9:28:6 | Uses Step | .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step |
| .github/workflows/artifactpoisoning92.yml:28:9:29:6 | Uses Step | .github/workflows/artifactpoisoning92.yml:29:9:29:27 | Run Step |
| .github/workflows/artifactpoisoning93.yml:12:9:13:6 | Uses Step | .github/workflows/artifactpoisoning93.yml:13:9:19:6 | Uses Step |
| .github/workflows/artifactpoisoning93.yml:13:9:19:6 | Uses Step | .github/workflows/artifactpoisoning93.yml:19:9:19:24 | Run Step |
| .github/workflows/artifactpoisoning94.yml:12:9:13:6 | Uses Step | .github/workflows/artifactpoisoning94.yml:13:9:19:6 | Uses Step |
| .github/workflows/artifactpoisoning94.yml:13:9:19:6 | Uses Step | .github/workflows/artifactpoisoning94.yml:19:9:19:24 | Run Step |
| .github/workflows/artifactpoisoning95.yml:12:9:13:6 | Uses Step | .github/workflows/artifactpoisoning95.yml:13:9:19:6 | Uses Step |
| .github/workflows/artifactpoisoning95.yml:13:9:19:6 | Uses Step | .github/workflows/artifactpoisoning95.yml:19:9:19:24 | Run Step |
| .github/workflows/artifactpoisoning96.yml:12:9:13:6 | Uses Step | .github/workflows/artifactpoisoning96.yml:13:9:18:6 | Uses Step |
| .github/workflows/artifactpoisoning96.yml:13:9:18:6 | Uses Step | .github/workflows/artifactpoisoning96.yml:18:9:18:24 | Run Step |
| .github/workflows/artifactpoisoning97.yml:12:9:13:6 | Uses Step | .github/workflows/artifactpoisoning97.yml:13:9:19:6 | Uses Step |
| .github/workflows/artifactpoisoning97.yml:13:9:19:6 | Uses Step | .github/workflows/artifactpoisoning97.yml:19:9:19:25 | Run Step |
| .github/workflows/artifactpoisoning101.yml:10:9:16:6 | Uses Step | .github/workflows/artifactpoisoning101.yml:16:9:19:59 | Run Step: pr_number |
| .github/workflows/auto_ci.yml:20:9:27:6 | Uses Step | .github/workflows/auto_ci.yml:27:9:32:6 | Uses Step |
| .github/workflows/auto_ci.yml:27:9:32:6 | Uses Step | .github/workflows/auto_ci.yml:32:9:37:6 | Run Step |

View File

@@ -1,13 +1,11 @@
{
"files": [
"cpp/ql/lib/semmlecode.cpp.dbscheme",
"javascript/ql/lib/semmlecode.javascript.dbscheme",
"python/ql/lib/semmlecode.python.dbscheme",
"ruby/ql/lib/ruby.dbscheme",
"ql/ql/src/ql.dbscheme"
],
"fragments": [
"/*- Compilations -*/",
"/*- External data -*/",
"/*- Files and folders -*/",
"/*- Diagnostic messages -*/",
@@ -23,7 +21,6 @@
"/*- DEPRECATED: Snapshot date -*/",
"/*- DEPRECATED: Duplicate code -*/",
"/*- DEPRECATED: Version control data -*/",
"/*- C++ dbscheme -*/",
"/*- JavaScript-specific part -*/",
"/*- Ruby dbscheme -*/",
"/*- Erb dbscheme -*/",

View File

@@ -231,10 +231,35 @@
"java/ql/src/experimental/Security/CWE/CWE-400/LocalThreadResourceAbuse.qhelp",
"java/ql/src/experimental/Security/CWE/CWE-400/ThreadResourceAbuse.qhelp"
],
"CryptoAlgorithms Python/JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll",
"ruby/ql/lib/codeql/ruby/security/CryptoAlgorithms.qll",
"rust/ql/lib/codeql/rust/security/CryptoAlgorithms.qll"
],
"CryptoAlgorithmNames Python/JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/internal/CryptoAlgorithmNames.qll",
"python/ql/lib/semmle/python/concepts/internal/CryptoAlgorithmNames.qll",
"ruby/ql/lib/codeql/ruby/security/internal/CryptoAlgorithmNames.qll",
"rust/ql/lib/codeql/rust/security/internal/CryptoAlgorithmNames.qll"
],
"SensitiveDataHeuristics Python/JS": [
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll",
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll",
"swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll",
"rust/ql/lib/codeql/rust/security/internal/SensitiveDataHeuristics.qll"
],
"IncompleteUrlSubstringSanitization": [
"javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll",
"ruby/ql/src/queries/security/cwe-020/IncompleteUrlSubstringSanitization.qll"
],
"Concepts Python/Ruby/JS": [
"python/ql/lib/semmle/python/internal/ConceptsShared.qll",
"ruby/ql/lib/codeql/ruby/internal/ConceptsShared.qll",
"javascript/ql/lib/semmle/javascript/internal/ConceptsShared.qll",
"rust/ql/lib/codeql/rust/internal/ConceptsShared.qll"
],
"ApiGraphModels": [
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll",
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll",

View File

@@ -2,9 +2,6 @@ language: cpp
strategy: dca
destination: cpp/ql/lib/ext/generated
targets:
- name: glibc
with-sinks: false
with-sources: false
- name: zlib
with-sinks: false
with-sources: false

View File

@@ -1,2 +0,0 @@
description: Uncomment cases in dbscheme
compatibility: full

View File

@@ -1,2 +0,0 @@
description: Remove unused external_package tables from the dbscheme
compatibility: full

View File

@@ -1,9 +0,0 @@
class Function extends @function {
string toString() { none() }
}
from Function f, string n, int k, int new_k
where
functions(f, n, k) and
if builtin_functions(f) then new_k = 6 else new_k = k
select f, n, new_k

View File

@@ -1,4 +0,0 @@
description: Move builtin function identification to its own table
compatibility: full
functions.rel: run functions.qlo
builtin_functions.rel: delete

View File

@@ -1,2 +0,0 @@
description: sync dbscheme and delete svn tables
compatibility: full

View File

@@ -1,161 +0,0 @@
class Accessible extends @accessible {
string toString() { none() }
}
class Container extends @container {
string toString() { none() }
}
class Expr extends @expr {
string toString() { none() }
}
class Initialiser extends @initialiser {
string toString() { none() }
}
class Location extends @location_default {
string toString() { none() }
}
class Stmt extends @stmt {
string toString() { none() }
}
predicate isLocationDefault(Location l) {
diagnostics(_, _, _, _, _, l)
or
macroinvocations(_, _, l, _)
or
fun_decls(_, _, _, _, l)
or
var_decls(_, _, _, _, l)
or
type_decls(_, _, l)
or
namespace_decls(_, _, l, _)
or
namespace_decls(_, _, _, l)
or
usings(_, _, l, _)
or
static_asserts(_, _, _, l, _)
or
enumconstants(_, _, _, _, _, l)
or
concept_templates(_, _, l)
or
attributes(_, _, _, _, l)
or
attribute_args(_, _, _, _, l)
or
derivations(_, _, _, _, l)
or
frienddecls(_, _, _, l)
or
comments(_, _, l)
or
namequalifiers(_, _, _, l)
or
lambda_capture(_, _, _, _, _, _, l)
or
preprocdirects(_, _, l)
or
xmllocations(_, l)
or
locations_default(l, _, 0, 0, 0, 0) // For containers.
}
predicate isLocationExpr(Location l) {
initialisers(_, _, _, l)
or
exprs(_, _, l)
}
predicate isLocationStmt(Location l) { stmts(_, _, l) }
newtype TExprOrStmtLocation =
TExprLocation(Location l, Container c, int startLine, int startColumn, int endLine, int endColumn) {
isLocationExpr(l) and
(isLocationDefault(l) or isLocationStmt(l)) and
locations_default(l, c, startLine, startColumn, endLine, endColumn)
} or
TStmtLocation(Location l, Container c, int startLine, int startColumn, int endLine, int endColumn) {
isLocationStmt(l) and
(isLocationDefault(l) or isLocationExpr(l)) and
locations_default(l, c, startLine, startColumn, endLine, endColumn)
}
module Fresh = QlBuiltins::NewEntity<TExprOrStmtLocation>;
class NewLocationBase = @location_default or Fresh::EntityId;
class NewLocation extends NewLocationBase {
string toString() { none() }
}
query predicate new_locations_default(
NewLocation l, Container c, int startLine, int startColumn, int endLine, int endColumn
) {
isLocationDefault(l) and
locations_default(l, c, startLine, startColumn, endLine, endColumn)
}
query predicate new_locations_expr(
NewLocation l, Container c, int startLine, int startColumn, int endLine, int endColumn
) {
exists(Location l_old |
isLocationExpr(l_old) and
locations_default(l_old, c, startLine, startColumn, endLine, endColumn)
|
if not isLocationDefault(l_old) and not isLocationStmt(l)
then l = l_old
else l = Fresh::map(TExprLocation(l_old, c, startLine, startColumn, endLine, endColumn))
)
}
query predicate new_locations_stmt(
NewLocation l, Container c, int startLine, int startColumn, int endLine, int endColumn
) {
exists(Location l_old |
isLocationStmt(l_old) and
locations_default(l_old, c, startLine, startColumn, endLine, endColumn)
|
if not isLocationDefault(l_old) and not isLocationExpr(l)
then l = l_old
else l = Fresh::map(TStmtLocation(l_old, c, startLine, startColumn, endLine, endColumn))
)
}
query predicate new_exprs(Expr e, int kind, NewLocation l) {
exists(Location l_old, Container c, int startLine, int startColumn, int endLine, int endColumn |
exprs(e, kind, l_old) and
locations_default(l_old, c, startLine, startColumn, endLine, endColumn)
|
if not isLocationDefault(l_old) and not isLocationStmt(l)
then l = l_old
else l = Fresh::map(TExprLocation(l_old, c, startLine, startColumn, endLine, endColumn))
)
}
query predicate new_initialisers(Initialiser i, Accessible v, Expr e, NewLocation l) {
exists(Location l_old, Container c, int startLine, int startColumn, int endLine, int endColumn |
initialisers(i, v, e, l_old) and
locations_default(l_old, c, startLine, startColumn, endLine, endColumn)
|
if not isLocationDefault(l_old) and not isLocationStmt(l)
then l = l_old
else l = Fresh::map(TExprLocation(l_old, c, startLine, startColumn, endLine, endColumn))
)
}
query predicate new_stmts(Stmt s, int kind, NewLocation l) {
exists(Location l_old, Container c, int startLine, int startColumn, int endLine, int endColumn |
stmts(s, kind, l_old) and
locations_default(l_old, c, startLine, startColumn, endLine, endColumn)
|
if not isLocationDefault(l_old) and not isLocationExpr(l)
then l = l_old
else l = Fresh::map(TStmtLocation(l_old, c, startLine, startColumn, endLine, endColumn))
)
}

View File

@@ -1,8 +0,0 @@
description: Merge location tables
compatibility: partial
locations_default.rel: run downgrades.ql new_locations_default
locations_expr.rel: run downgrades.ql new_locations_expr
locations_stmt.rel: run downgrades.ql new_locations_stmt
exprs.rel: run downgrades.ql new_exprs
initialisers.rel: run downgrades.ql new_initialisers
stmts.rel: run downgrades.ql new_stmts

View File

@@ -4,3 +4,4 @@
int main() {
return ONE + TWO + THREE + FOUR;
}
// semmle-extractor-options: --clang -include-pch ${testdir}/clang-pch.testproj/a.pch -Iextra_dummy_path

View File

@@ -1,20 +1,3 @@
## 5.3.0
### Deprecated APIs
* The `UnknownDefaultLocation`, `UnknownExprLocation`, and `UnknownStmtLocation` classes have been deprecated. Use `UnknownLocation` instead.
### New Features
* Added a `isFinalValueOfParameter` predicate to `DataFlow::Node` which holds when a dataflow node represents the final value of an output parameter of a function.
### Minor Analysis Improvements
* The `FunctionWithWrappers` library (`semmle.code.cpp.security.FunctionWithWrappers`) no longer considers calls through function pointers as wrapper functions.
* The analysis of C/C++ code targeting 64-bit Arm platforms has been improved. This includes support for the Arm-specific builtin functions, support for the `arm_neon.h` header and Neon vector types, and support for the `fp8` scalar type. The `arm_sve.h` header and scalable vectors are only partially supported at this point.
* Added support for `__fp16 _Complex` and `__bf16 _Complex` types
* Added `sql-injection` sink models for the Oracle Call Interface (OCI) database library functions `OCIStmtPrepare` and `OCIStmtPrepare2`.
## 5.2.0
### Deprecated APIs

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added `sql-injection` sink models for the Oracle Call Interface (OCI) database library functions `OCIStmtPrepare` and `OCIStmtPrepare2`.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added support for `__fp16 _Complex` and `__bf16 _Complex` types

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The `cpp/overrun-write` query now recognizes more bound checks and thus produces fewer false positives.

View File

@@ -1,16 +0,0 @@
## 5.3.0
### Deprecated APIs
* The `UnknownDefaultLocation`, `UnknownExprLocation`, and `UnknownStmtLocation` classes have been deprecated. Use `UnknownLocation` instead.
### New Features
* Added a `isFinalValueOfParameter` predicate to `DataFlow::Node` which holds when a dataflow node represents the final value of an output parameter of a function.
### Minor Analysis Improvements
* The `FunctionWithWrappers` library (`semmle.code.cpp.security.FunctionWithWrappers`) no longer considers calls through function pointers as wrapper functions.
* The analysis of C/C++ code targeting 64-bit Arm platforms has been improved. This includes support for the Arm-specific builtin functions, support for the `arm_neon.h` header and Neon vector types, and support for the `fp8` scalar type. The `arm_sve.h` header and scalable vectors are only partially supported at this point.
* Added support for `__fp16 _Complex` and `__bf16 _Complex` types
* Added `sql-injection` sink models for the Oracle Call Interface (OCI) database library functions `OCIStmtPrepare` and `OCIStmtPrepare2`.

View File

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

View File

@@ -8,7 +8,7 @@ module CryptoInput implements InputSig<Language::Location> {
class LocatableElement = Language::Locatable;
class UnknownLocation = Language::UnknownLocation;
class UnknownLocation = Language::UnknownDefaultLocation;
LocatableElement dfn_to_element(DataFlow::Node node) {
result = node.asExpr() or

View File

@@ -54,15 +54,13 @@ class KnownOpenSslHMacConstantAlgorithmInstance extends Crypto::HmacAlgorithmIns
then
// ASSUMPTION: if there is an explicit hash algorithm, it is already modeled
// and we can simply grab that model's AVC
this.(OpenSslAlgorithmInstance).getAvc() = result
exists(OpenSslAlgorithmInstance inst | inst.getAvc() = result and inst = this)
else
// ASSUMPTION: If no explicit algorithm is given, then find
// where the current AVC traces to a HashAlgorithmIO consuming operation step.
// TODO: need to consider getting reset values, tracing down to the first set for now
exists(OperationStep s, AvcContextCreationStep avc |
avc = this.getAvc() and
avc.flowsToOperationStep(s) and
s.getAlgorithmValueConsumerForInput(HashAlgorithmIO()) = result
// ASSUMPTION: If no explicit algorithm is given, then it is assumed to be configured by
// a signature operation
exists(Crypto::SignatureOperationInstance s |
s.getHashAlgorithmValueConsumer() = result and
s.getAnAlgorithmValueConsumer() = this.getAvc()
)
}
}

View File

@@ -0,0 +1,221 @@
//TODO: model as data on open APIs should be able to get common flows, and obviate some of this
// e.g., copy/dup calls, need to ingest those models for openSSL and refactor.
/**
* In OpenSSL, flow between 'context' parameters is often used to
* store state/config of how an operation will eventually be performed.
* Tracing algorithms and configurations to operations therefore
* requires tracing context parameters for many OpenSSL apis.
*
* This library provides a dataflow analysis to track context parameters
* between any two functions accepting openssl context parameters.
* The dataflow takes into consideration flowing through duplication and copy calls
* as well as flow through flow killers (free/reset calls).
*
* TODO: we may need to revisit 'free' as a dataflow killer, depending on how
* we want to model use after frees.
*
* This library also provides classes to represent context Types and relevant
* arguments/expressions.
*/
import semmle.code.cpp.dataflow.new.DataFlow
/**
* An openSSL CTX type, which is type for which the stripped underlying type
* matches the pattern 'evp_%ctx_%st'.
* This includes types like:
* - EVP_CIPHER_CTX
* - EVP_MD_CTX
* - EVP_PKEY_CTX
*/
class CtxType extends Type {
CtxType() {
// It is possible for users to use the underlying type of the CTX variables
// these have a name matching 'evp_%ctx_%st
this.getUnspecifiedType().stripType().getName().matches("evp_%ctx_%st")
or
// In principal the above check should be sufficient, but in case of build mode none issues
// i.e., if a typedef cannot be resolved,
// or issues with properly stubbing test cases, we also explicitly check for the wrapping type defs
// i.e., patterns matching 'EVP_%_CTX'
exists(Type base | base = this or base = this.(DerivedType).getBaseType() |
base.getName().matches("EVP_%_CTX")
)
}
}
/**
* A pointer to a CtxType
*/
class CtxPointerExpr extends Expr {
CtxPointerExpr() {
this.getType() instanceof CtxType and
this.getType() instanceof PointerType
}
}
/**
* A call argument of type CtxPointerExpr.
*/
class CtxPointerArgument extends CtxPointerExpr {
CtxPointerArgument() { exists(Call c | c.getAnArgument() = this) }
Call getCall() { result.getAnArgument() = this }
}
/**
* A call returning a CtxPointerExpr.
*/
private class CtxPointerReturn extends CtxPointerExpr instanceof Call {
Call getCall() { result = this }
}
/**
* A call whose target contains 'free' or 'reset' and has an argument of type
* CtxPointerArgument.
*/
private class CtxClearCall extends Call {
CtxClearCall() {
this.getTarget().getName().toLowerCase().matches(["%free%", "%reset%"]) and
this.getAnArgument() instanceof CtxPointerArgument
}
}
abstract private class CtxPassThroughCall extends Call {
abstract DataFlow::Node getNode1();
abstract DataFlow::Node getNode2();
}
/**
* A call whose target contains 'copy' and has an argument of type
* CtxPointerArgument.
*/
private class CtxCopyOutArgCall extends CtxPassThroughCall {
DataFlow::Node n1;
DataFlow::Node n2;
CtxCopyOutArgCall() {
this.getTarget().getName().toLowerCase().matches("%copy%") and
n1.asExpr() = this.getAnArgument() and
n1.getType() instanceof CtxType and
n2.asDefiningArgument() = this.getAnArgument() and
n2.getType() instanceof CtxType and
n1.asDefiningArgument() != n2.asExpr()
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
/**
* A call whose target contains 'dup' and has an argument of type
* CtxPointerArgument.
*/
private class CtxCopyReturnCall extends CtxPassThroughCall, CtxPointerExpr {
DataFlow::Node n1;
CtxCopyReturnCall() {
this.getTarget().getName().toLowerCase().matches("%dup%") and
n1.asExpr() = this.getAnArgument() and
n1.getType() instanceof CtxType
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result.asExpr() = this }
}
/**
* A call to `EVP_PKEY_paramgen` acts as a kind of pass through.
* It's output pkey is eventually used in a new operation generating
* a fresh context pointer (e.g., `EVP_PKEY_CTX_new`).
* It is easier to model this as a pass through
* than to model the flow from the paramgen to the new key generation.
*/
private class CtxParamGenCall extends CtxPassThroughCall {
DataFlow::Node n1;
DataFlow::Node n2;
CtxParamGenCall() {
this.getTarget().getName() = "EVP_PKEY_paramgen" and
n1.asExpr() = this.getArgument(0) and
(
n2.asExpr() = this.getArgument(1)
or
n2.asDefiningArgument() = this.getArgument(1)
)
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
/**
* If the current node gets is an argument to a function
* that returns a pointer type, immediately flow through.
* NOTE: this passthrough is required if we allow
* intermediate steps to go into variables that are not a CTX type.
* See for example `CtxParamGenCall`.
*/
private class CallArgToCtxRet extends CtxPassThroughCall, CtxPointerExpr {
DataFlow::Node n1;
DataFlow::Node n2;
CallArgToCtxRet() {
this.getAnArgument() = n1.asExpr() and
n2.asExpr() = this
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
/**
* A source Ctx of interest is any argument or return of type CtxPointerExpr.
*/
class CtxPointerSource extends CtxPointerExpr {
CtxPointerSource() {
this instanceof CtxPointerReturn or
this instanceof CtxPointerArgument
}
DataFlow::Node asNode() {
result.asExpr() = this
or
result.asDefiningArgument() = this
}
}
/**
* Flow from any CtxPointerSource to other CtxPointerSource.
*/
module OpenSslCtxSourceToSourceFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { exists(CtxPointerSource s | s.asNode() = source) }
predicate isSink(DataFlow::Node sink) { exists(CtxPointerSource s | s.asNode() = sink) }
predicate isBarrier(DataFlow::Node node) {
exists(CtxClearCall c | c.getAnArgument() = node.asExpr())
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2)
}
}
module OpenSslCtxSourceToArgumentFlow = DataFlow::Global<OpenSslCtxSourceToSourceFlowConfig>;
/**
* Holds if there is a context flow from the source to the sink.
*/
predicate ctxSrcToSrcFlow(CtxPointerSource source, CtxPointerSource sink) {
exists(DataFlow::Node a, DataFlow::Node b |
OpenSslCtxSourceToArgumentFlow::flow(a, b) and
a = source.asNode() and
b = sink.asNode()
)
}

View File

@@ -1,273 +0,0 @@
import experimental.quantum.Language
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
import EVPPKeyCtxInitializer
/**
* A base class for all EVP cipher operations.
*/
abstract class EvpCipherInitializer extends OperationStep {
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and
type = PrimaryAlgorithmIO() and
// Constants that are not equal to zero or
// non-constants (e.g., variable accesses, which require data-flow to determine the value)
// A zero (null) value typically indicates use of this operation step to initialize
// other out parameters in a multi-step initialization.
(exists(result.asExpr().getValue()) implies result.asExpr().getValue().toInt() != 0)
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
/**
* A base class for EVP cipher/decrypt/encrypt 'ex' operations.
*/
abstract class EvpEXInitializer extends EvpCipherInitializer {
override DataFlow::Node getInput(IOType type) {
result = super.getInput(type)
or
(
// Constants that are not equal to zero or
// non-constants (e.g., variable accesses, which require data-flow to determine the value)
// A zero (null) value typically indicates use of this operation step to initialize
// other out parameters in a multi-step initialization.
result.asExpr() = this.getArgument(3) and type = KeyIO()
or
result.asExpr() = this.getArgument(4) and type = IVorNonceIO()
) and
(exists(result.asExpr().getValue()) implies result.asExpr().getValue().toInt() != 0)
}
}
/**
* A base class for EVP cipher/decrypt/encrypt 'ex2' operations.
*/
abstract class EvpEX2Initializer extends EvpCipherInitializer {
override DataFlow::Node getInput(IOType type) {
result = super.getInput(type)
or
result.asExpr() = this.getArgument(2) and type = KeyIO()
or
result.asExpr() = this.getArgument(3) and type = IVorNonceIO()
}
}
/**
* A Call to an EVP Cipher/Encrypt/Decrypt initialization operation.
*/
class EvpCipherEXInitCall extends EvpEXInitializer {
EvpCipherEXInitCall() {
this.getTarget().getName() in ["EVP_EncryptInit_ex", "EVP_DecryptInit_ex", "EVP_CipherInit_ex"]
}
override DataFlow::Node getInput(IOType type) {
result = super.getInput(type)
or
// NOTE: for EncryptInit and DecryptInit there is no subtype arg
// the subtype is determined automatically by the initializer based on the operation name
this.getTarget().getName().toLowerCase().matches("%cipherinit%") and
result.asExpr() = this.getArgument(5) and
type = KeyOperationSubtypeIO()
}
}
class Evp_Cipher_EX2_or_Simple_Init_Call extends EvpEX2Initializer {
Evp_Cipher_EX2_or_Simple_Init_Call() {
this.getTarget().getName() in [
"EVP_EncryptInit_ex2", "EVP_DecryptInit_ex2", "EVP_CipherInit_ex2", "EVP_EncryptInit",
"EVP_DecryptInit", "EVP_CipherInit"
]
}
override DataFlow::Node getInput(IOType type) {
result = super.getInput(type)
or
this.getTarget().getName().toLowerCase().matches("%cipherinit%") and
result.asExpr() = this.getArgument(4) and
type = KeyOperationSubtypeIO()
}
}
/**
* A call to EVP_Pkey_encrypt_init, EVP_Pkey_decrypt_init, or their 'ex' variants.
*/
class EvpPkeyEncryptDecryptInit extends OperationStep {
EvpPkeyEncryptDecryptInit() {
this.getTarget().getName() in [
"EVP_PKEY_encrypt_init", "EVP_PKEY_encrypt_init_ex", "EVP_PKEY_decrypt_init",
"EVP_PKEY_decrypt_init_ex"
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = OsslParamIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
class EvpCipherInitSKeyCall extends EvpEX2Initializer {
EvpCipherInitSKeyCall() { this.getTarget().getName() = "EVP_CipherInit_SKEY" }
override DataFlow::Node getInput(IOType type) {
result = super.getInput(type)
or
result.asExpr() = this.getArgument(5) and
type = KeyOperationSubtypeIO()
}
}
//EVP_PKEY_encrypt_init
/**
* A Call to EVP_Cipher/Encrypt/DecryptUpdate.
* https://docs.openssl.org/3.2/man3/EVP_CipherUpdate
*/
class EvpCipherUpdateCall extends OperationStep {
EvpCipherUpdateCall() {
this.getTarget().getName() in ["EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate"]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(3) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(1) and type = CiphertextIO()
or
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = UpdateStep() }
}
/**
* A base configuration for all EVP cipher operations.
*/
abstract class EvpCipherOperationFinalStep extends OperationStep {
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = FinalStep() }
}
/**
* A Call to EVP_Cipher.
*/
class EvpCipherCall extends EvpCipherOperationFinalStep {
EvpCipherCall() { this.getTarget().getName() = "EVP_Cipher" }
override DataFlow::Node getInput(IOType type) {
super.getInput(type) = result
or
result.asExpr() = this.getArgument(2) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
super.getOutput(type) = result
or
result.asExpr() = this.getArgument(1) and type = CiphertextIO()
}
}
/**
* A Call to an EVP Cipher/Encrypt/Decrypt final operation.
*/
class EvpCipherFinalCall extends EvpCipherOperationFinalStep {
EvpCipherFinalCall() {
this.getTarget().getName() in [
"EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal",
"EVP_DecryptFinal", "EVP_CipherFinal"
]
}
override DataFlow::Node getOutput(IOType type) {
super.getOutput(type) = result
or
result.asDefiningArgument() = this.getArgument(1) and
type = CiphertextIO()
// TODO: could indicate text lengths here, as well
}
}
/**
* A call to a PKEY_encrypt or PKEY_decrypt operation.
* https://docs.openssl.org/3.2/man3/EVP_PKEY_decrypt/
* https://docs.openssl.org/3.2/man3/EVP_PKEY_encrypt
*/
class EvpPKeyCipherOperation extends EvpCipherOperationFinalStep {
EvpPKeyCipherOperation() {
this.getTarget().getName() in ["EVP_PKEY_encrypt", "EVP_PKEY_decrypt"]
}
override DataFlow::Node getInput(IOType type) {
super.getInput(type) = result
or
result.asExpr() = this.getArgument(3) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
super.getOutput(type) = result
or
result.asExpr() = this.getArgument(1) and type = CiphertextIO()
// TODO: could indicate text lengths here, as well
}
}
/**
* An EVP cipher operation instance.
* Any operation step that is a final operation step for EVP cipher operation steps.
*/
class EvpCipherOperationInstance extends Crypto::KeyOperationInstance instanceof EvpCipherOperationFinalStep
{
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
super.getPrimaryAlgorithmValueConsumer() = result
}
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
result instanceof Crypto::TEncryptMode and
super.getTarget().getName().toLowerCase().matches("%encrypt%")
or
result instanceof Crypto::TDecryptMode and
super.getTarget().getName().toLowerCase().matches("%decrypt%")
or
super.getTarget().getName().toLowerCase().matches("%cipher%") and
resolveKeyOperationSubTypeOperationStep(super
.getDominatingInitializersToStep(KeyOperationSubtypeIO())) = result
}
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
super.getDominatingInitializersToStep(IVorNonceIO()).getInput(IVorNonceIO()) = result
}
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
super.getDominatingInitializersToStep(KeyIO()).getInput(KeyIO()) = result
}
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
super.getOutputStepFlowingToStep(CiphertextIO()).getOutput(CiphertextIO()) = result
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
super.getDominatingInitializersToStep(PlaintextIO()).getInput(PlaintextIO()) = result
}
}

View File

@@ -0,0 +1,33 @@
private import experimental.quantum.Language
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
private import semmle.code.cpp.dataflow.new.DataFlow
class ECKeyGenOperation extends OpenSslOperation, Crypto::KeyGenerationOperationInstance {
ECKeyGenOperation() { this.(Call).getTarget().getName() = "EC_KEY_generate_key" }
override Expr getAlgorithmArg() { result = this.(Call).getArgument(0) }
override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() }
override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() {
result.asExpr() = this.(Call).getArgument(0)
}
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
none() // no explicit key size, inferred from algorithm
}
override int getKeySizeFixed() {
none()
// TODO: marked as none as the operation itself has no key size, it
// comes from the algorithm source, but note we could grab the
// algorithm source and get the key size (see below).
// We may need to reconsider what is the best approach here.
// result =
// this.getAnAlgorithmValueConsumer()
// .getAKnownAlgorithmSource()
// .(Crypto::EllipticCurveInstance)
// .getKeySize()
}
}

View File

@@ -0,0 +1,183 @@
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.CtxFlow
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
// TODO: need to add key consumer
abstract class Evp_Cipher_Initializer extends EvpKeyOperationSubtypeInitializer,
EvpPrimaryAlgorithmInitializer, EvpKeyInitializer, EvpIVInitializer
{
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
}
abstract class Evp_EX_Initializer extends Evp_Cipher_Initializer {
override Expr getKeyArg() {
// Null key indicates the key is not actually set
// This pattern can occur during a multi-step initialization
// TODO/Note: not flowing 0 to the sink, assuming a direct use of NULL for now
result = this.(Call).getArgument(3) and
(exists(result.getValue()) implies result.getValue().toInt() != 0)
}
override Expr getIVArg() {
// Null IV indicates the IV is not actually set
// This occurs given that setting the IV sometimes requires first setting the IV size.
// TODO/Note: not flowing 0 to the sink, assuming a direct use of NULL for now
result = this.(Call).getArgument(4) and
(exists(result.getValue()) implies result.getValue().toInt() != 0)
}
}
abstract class Evp_EX2_Initializer extends Evp_Cipher_Initializer {
override Expr getKeyArg() { result = this.(Call).getArgument(2) }
override Expr getIVArg() { result = this.(Call).getArgument(3) }
}
class EvpCipherEXInitCall extends Evp_EX_Initializer {
EvpCipherEXInitCall() {
this.(Call).getTarget().getName() in [
"EVP_EncryptInit_ex", "EVP_DecryptInit_ex", "EVP_CipherInit_ex"
]
}
override Expr getKeyOperationSubtypeArg() {
// NOTE: for EncryptInit and DecryptInit there is no subtype arg
// the subtype is determined automatically by the initializer based on the operation name
this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and
result = this.(Call).getArgument(5)
}
}
// if this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%")
// then result instanceof Crypto::TEncryptMode
// else
// if this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%")
// then result instanceof Crypto::TDecryptMode
class Evp_Cipher_EX2_or_Simple_Init_Call extends Evp_EX2_Initializer {
Evp_Cipher_EX2_or_Simple_Init_Call() {
this.(Call).getTarget().getName() in [
"EVP_EncryptInit_ex2", "EVP_DecryptInit_ex2", "EVP_CipherInit_ex2", "EVP_EncryptInit",
"EVP_DecryptInit", "EVP_CipherInit"
]
}
override Expr getKeyOperationSubtypeArg() {
this.(Call).getTarget().getName().toLowerCase().matches("%cipherinit%") and
result = this.(Call).getArgument(4)
}
}
class Evp_CipherInit_SKey_Call extends Evp_EX2_Initializer {
Evp_CipherInit_SKey_Call() { this.(Call).getTarget().getName() = "EVP_CipherInit_SKEY" }
override Expr getKeyOperationSubtypeArg() { result = this.(Call).getArgument(5) }
}
class Evp_Cipher_Update_Call extends EvpUpdate {
Evp_Cipher_Update_Call() {
this.(Call).getTarget().getName() in [
"EVP_EncryptUpdate", "EVP_DecryptUpdate", "EVP_CipherUpdate"
]
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getInputArg() { result = this.(Call).getArgument(3) }
override Expr getOutputArg() { result = this.(Call).getArgument(1) }
}
/**
* The EVP Cipher operations.
* See: https://docs.openssl.org/master/man3/EVP_EncryptInit/#synopsis
* Base configuration for all EVP cipher operations.
*/
abstract class Evp_Cipher_Operation extends EvpOperation, Crypto::KeyOperationInstance {
override Expr getOutputArg() { result = this.(Call).getArgument(1) }
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
result instanceof Crypto::TEncryptMode and
this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%")
or
result instanceof Crypto::TDecryptMode and
this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%")
or
result = this.getInitCall().(EvpKeyOperationSubtypeInitializer).getKeyOperationSubtype() and
this.(Call).getTarget().getName().toLowerCase().matches("%cipher%")
}
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
this.getInitCall().(EvpIVInitializer).getIVArg() = result.asExpr()
}
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
this.getInitCall().(EvpKeyInitializer).getKeyArg() = result.asExpr()
// todo: or track to the EVP_PKEY_CTX_new
}
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = EvpOperation.super.getOutputArtifact()
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = EvpOperation.super.getInputConsumer()
}
}
class Evp_Cipher_Call extends EvpOperation, Evp_Cipher_Operation {
Evp_Cipher_Call() { this.(Call).getTarget().getName() = "EVP_Cipher" }
override Expr getInputArg() { result = this.(Call).getArgument(2) }
override Expr getAlgorithmArg() {
result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg()
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class Evp_Cipher_Final_Call extends EvpFinal, Evp_Cipher_Operation {
Evp_Cipher_Final_Call() {
this.(Call).getTarget().getName() in [
"EVP_EncryptFinal_ex", "EVP_DecryptFinal_ex", "EVP_CipherFinal_ex", "EVP_EncryptFinal",
"EVP_DecryptFinal", "EVP_CipherFinal"
]
}
/**
* Output is both from update calls and from the final call.
*/
override Expr getOutputArg() {
result = EvpFinal.super.getOutputArg()
or
result = Evp_Cipher_Operation.super.getOutputArg()
}
override Expr getAlgorithmArg() {
result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg()
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
/**
* The EVP encryption/decryption operations.
* https://docs.openssl.org/3.2/man3/EVP_PKEY_decrypt/
* https://docs.openssl.org/3.2/man3/EVP_PKEY_encrypt
*/
class Evp_PKey_Cipher_Operation extends Evp_Cipher_Operation {
Evp_PKey_Cipher_Operation() {
this.(Call).getTarget().getName() in ["EVP_PKEY_encrypt", "EVP_PKEY_decrypt"]
}
override Expr getInputArg() { result = this.(Call).getArgument(3) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getAlgorithmArg() {
result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg()
}
}

View File

@@ -0,0 +1,106 @@
/**
* https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis
*/
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.CtxFlow
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
class Evp_DigestInit_Variant_Calls extends EvpPrimaryAlgorithmInitializer {
Evp_DigestInit_Variant_Calls() {
this.(Call).getTarget().getName() in [
"EVP_DigestInit", "EVP_DigestInit_ex", "EVP_DigestInit_ex2"
]
}
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class Evp_Digest_Update_Call extends EvpUpdate {
Evp_Digest_Update_Call() { this.(Call).getTarget().getName() = "EVP_DigestUpdate" }
override Expr getInputArg() { result = this.(Call).getArgument(1) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
//https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis
class Evp_Q_Digest_Operation extends EvpOperation, Crypto::HashOperationInstance {
Evp_Q_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Q_digest" }
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
override EvpInitializer getInitCall() {
// This variant of digest does not use an init
// and even if it were used, the init would be ignored/undefined
none()
}
override Expr getInputArg() { result = this.(Call).getArgument(3) }
override Expr getOutputArg() { result = this.(Call).getArgument(5) }
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = EvpOperation.super.getOutputArtifact()
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = EvpOperation.super.getInputConsumer()
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class Evp_Digest_Operation extends EvpOperation, Crypto::HashOperationInstance {
Evp_Digest_Operation() { this.(Call).getTarget().getName() = "EVP_Digest" }
// There is no context argument for this function
override CtxPointerSource getContext() { none() }
override Expr getAlgorithmArg() { result = this.(Call).getArgument(4) }
override EvpPrimaryAlgorithmInitializer getInitCall() {
// This variant of digest does not use an init
// and even if it were used, the init would be ignored/undefined
none()
}
override Expr getInputArg() { result = this.(Call).getArgument(0) }
override Expr getOutputArg() { result = this.(Call).getArgument(2) }
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = EvpOperation.super.getOutputArtifact()
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = EvpOperation.super.getInputConsumer()
}
}
class Evp_Digest_Final_Call extends EvpFinal, Crypto::HashOperationInstance {
Evp_Digest_Final_Call() {
this.(Call).getTarget().getName() in [
"EVP_DigestFinal", "EVP_DigestFinal_ex", "EVP_DigestFinalXOF"
]
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getOutputArg() { result = this.(Call).getArgument(1) }
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = EvpFinal.super.getOutputArtifact()
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = EvpFinal.super.getInputConsumer()
}
override Expr getAlgorithmArg() {
result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg()
}
}

View File

@@ -0,0 +1,96 @@
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.CtxFlow
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
class EvpKeyGenInitialize extends EvpPrimaryAlgorithmInitializer {
EvpKeyGenInitialize() {
this.(Call).getTarget().getName() in [
"EVP_PKEY_keygen_init",
"EVP_PKEY_paramgen_init"
]
}
/**
* Gets the algorithm argument.
* In this case the algorithm is encoded through the context argument.
* The context may be directly created from an algorithm consumer,
* or from a new operation off of a prior key. Either way,
* we will treat this argument as the algorithm argument.
*/
override Expr getAlgorithmArg() { result = this.getContext() }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpKeyGenOperation extends EvpOperation, Crypto::KeyGenerationOperationInstance {
DataFlow::Node keyResultNode;
EvpKeyGenOperation() {
this.(Call).getTarget().getName() in ["EVP_RSA_gen", "EVP_PKEY_Q_keygen"] and
keyResultNode.asExpr() = this
or
this.(Call).getTarget().getName() in ["EVP_PKEY_generate", "EVP_PKEY_keygen"] and
keyResultNode.asDefiningArgument() = this.(Call).getArgument(1)
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getAlgorithmArg() {
this.(Call).getTarget().getName() = "EVP_PKEY_Q_keygen" and
result = this.(Call).getArgument(0)
or
result = this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg()
}
override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() }
override Expr getInputArg() { none() }
override Expr getOutputArg() { result = keyResultNode.asExpr() }
override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result = keyResultNode }
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
this.(Call).getTarget().getName() = "EVP_PKEY_Q_keygen" and
result = DataFlow::exprNode(this.(Call).getArgument(3)) and
// Arg 3 (0 based) is only a key size if the 'type' parameter is RSA, however,
// as a crude approximation, assume that if the type of the argument is not a derived type
// the argument must specify a key size (this is to avoid tracing if "rsa" is in the type parameter)
not this.(Call).getArgument(3).getType().getUnderlyingType() instanceof DerivedType
or
this.(Call).getTarget().getName() = "EVP_RSA_gen" and
result = DataFlow::exprNode(this.(Call).getArgument(0))
or
result = DataFlow::exprNode(this.getInitCall().(EvpKeySizeInitializer).getKeySizeArg())
}
}
/**
* A call to `EVP_PKEY_new_mac_key` that creatse a new generic MAC key.
* Signature: EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *e, const unsigned char *key, int keylen);
*/
class EvpNewMacKey extends EvpOperation, Crypto::KeyGenerationOperationInstance {
DataFlow::Node keyResultNode;
EvpNewMacKey() {
this.(Call).getTarget().getName() = "EVP_PKEY_new_mac_key" and keyResultNode.asExpr() = this
}
override CtxPointerSource getContext() { none() }
override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TSymmetricKeyType() }
override Expr getOutputArg() { result = keyResultNode.asExpr() }
override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() { result = keyResultNode }
override Expr getInputArg() { none() }
override Expr getAlgorithmArg() { result = this.(Call).getArgument(0) }
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
result = DataFlow::exprNode(this.(Call).getArgument(3))
}
}
/// TODO: https://docs.openssl.org/3.0/man3/EVP_PKEY_new/#synopsis

View File

@@ -6,6 +6,7 @@
*/
import cpp
private import experimental.quantum.OpenSSL.CtxFlow
private import OpenSSLOperations
/**
@@ -13,66 +14,49 @@ private import OpenSSLOperations
* These calls initialize the context from a prior key.
* The key may be generated previously, or merely had it's
* parameters set (e.g., `EVP_PKEY_paramgen`).
* NOTE: for the case of `EVP_PKEY_paramgen`, these calls
* are encoded as context passthroughs, and any operation
* will get all associated initializers for the paramgen
* at the final keygen operation automatically.
*/
class EvpNewKeyCtx extends OperationStep instanceof Call {
class EvpNewKeyCtx extends EvpKeyInitializer {
Expr keyArg;
EvpNewKeyCtx() {
this.getTarget().getName() = "EVP_PKEY_CTX_new" and
keyArg = this.getArgument(0)
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_new" and
keyArg = this.(Call).getArgument(0)
or
this.getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and
keyArg = this.getArgument(1)
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and
keyArg = this.(Call).getArgument(1)
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = keyArg and type = KeyIO()
or
this.getTarget().getName() = "EVP_PKEY_CTX_new_from_pkey" and
result.asExpr() = this.getArgument(0) and
type = OsslLibContextIO()
}
/**
* Context is returned
*/
override CtxPointerSource getContext() { result = this }
override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = ContextIO() }
override OperationStepType getStepType() { result = ContextCreationStep() }
override Expr getKeyArg() { result = keyArg }
}
/**
* A call to "EVP_PKEY_CTX_set_ec_paramgen_curve_nid".
* Note that this is a primary algorithm as the pattenr is to specify an "EC" context,
* then set the specific curve later. Although the curve is set later, it is the primary
* algorithm intended for an operation.
*/
class EvpCtxSetEcParamgenCurveNidInitializer extends OperationStep {
EvpCtxSetEcParamgenCurveNidInitializer() {
this.getTarget().getName() = "EVP_PKEY_CTX_set_ec_paramgen_curve_nid"
class EvpCtxSetPrimaryAlgorithmInitializer extends EvpPrimaryAlgorithmInitializer {
EvpCtxSetPrimaryAlgorithmInitializer() {
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_ec_paramgen_curve_nid"
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = PrimaryAlgorithmIO()
}
override Expr getAlgorithmArg() { result = this.(Call).getArgument(1) }
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
/**
* A call to the following:
* - `EVP_PKEY_CTX_set_signature_md`
* - `EVP_PKEY_CTX_set_rsa_mgf1_md_name`
* - `EVP_PKEY_CTX_set_rsa_mgf1_md`
* - `EVP_PKEY_CTX_set_rsa_oaep_md_name`
* - `EVP_PKEY_CTX_set_rsa_oaep_md`
* - `EVP_PKEY_CTX_set_dsa_paramgen_md`
* - `EVP_PKEY_CTX_set_dh_kdf_md`
* - `EVP_PKEY_CTX_set_ecdh_kdf_md`
*/
class EvpCtxSetHashInitializer extends OperationStep {
EvpCtxSetHashInitializer() {
this.getTarget().getName() in [
class EvpCtxSetHashAlgorithmInitializer extends EvpHashAlgorithmInitializer {
EvpCtxSetHashAlgorithmInitializer() {
this.(Call).getTarget().getName() in [
"EVP_PKEY_CTX_set_signature_md", "EVP_PKEY_CTX_set_rsa_mgf1_md_name",
"EVP_PKEY_CTX_set_rsa_mgf1_md", "EVP_PKEY_CTX_set_rsa_oaep_md_name",
"EVP_PKEY_CTX_set_rsa_oaep_md", "EVP_PKEY_CTX_set_dsa_paramgen_md",
@@ -80,95 +64,56 @@ class EvpCtxSetHashInitializer extends OperationStep {
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = HashAlgorithmIO()
}
override Expr getHashAlgorithmArg() { result = this.(Call).getArgument(1) }
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
/**
* A call to `EVP_PKEY_CTX_set_rsa_keygen_bits`, `EVP_PKEY_CTX_set_dsa_paramgen_bits`,
* or `EVP_CIPHER_CTX_set_key_length`.
*/
class EvpCtxSetKeySizeInitializer extends OperationStep {
class EvpCtxSetKeySizeInitializer extends EvpKeySizeInitializer {
Expr arg;
EvpCtxSetKeySizeInitializer() {
this.getTarget().getName() in [
this.(Call).getTarget().getName() in [
"EVP_PKEY_CTX_set_rsa_keygen_bits", "EVP_PKEY_CTX_set_dsa_paramgen_bits",
"EVP_CIPHER_CTX_set_key_length"
] and
arg = this.(Call).getArgument(1)
or
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_mac_key" and
arg = this.(Call).getArgument(2)
}
override Expr getKeySizeArg() { result = arg }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpCtxSetKeyInitializer extends EvpKeyInitializer {
EvpCtxSetKeyInitializer() { this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_mac_key" }
override Expr getKeyArg() { result = this.(Call).getArgument(1) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpCtxSetPaddingInitializer extends EvpPaddingInitializer {
EvpCtxSetPaddingInitializer() {
this.(Call).getTarget().getName() in [
"EVP_PKEY_CTX_set_rsa_padding", "EVP_CIPHER_CTX_set_padding"
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = KeySizeIO()
}
override Expr getPaddingArg() { result = this.(Call).getArgument(1) }
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpCtxSetMacKeyInitializer extends OperationStep {
EvpCtxSetMacKeyInitializer() { this.getTarget().getName() = "EVP_PKEY_CTX_set_mac_key" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(2) and type = KeySizeIO()
or
// the raw key that is configured into the output key
result.asExpr() = this.getArgument(1) and type = KeyIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
class EvpCtxSetPaddingInitializer extends OperationStep {
EvpCtxSetPaddingInitializer() {
this.getTarget().getName() in ["EVP_PKEY_CTX_set_rsa_padding", "EVP_CIPHER_CTX_set_padding"]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = PaddingAlgorithmIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
class EvpCtxSetSaltLengthInitializer extends OperationStep {
class EvpCtxSetSaltLengthInitializer extends EvpSaltLengthInitializer {
EvpCtxSetSaltLengthInitializer() {
this.getTarget().getName() = "EVP_PKEY_CTX_set_rsa_pss_saltlen"
this.(Call).getTarget().getName() = "EVP_PKEY_CTX_set_rsa_pss_saltlen"
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = SaltLengthIO()
}
override Expr getSaltLengthArg() { result = this.(Call).getArgument(1) }
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}

View File

@@ -0,0 +1,200 @@
/**
* Provides classes for modeling OpenSSL's EVP signature operations
*/
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.AvcFlow
private import experimental.quantum.OpenSSL.CtxFlow
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
private import experimental.quantum.OpenSSL.Operations.OpenSSLOperations
// TODO: verification functions
class EvpSignatureDigestInitializer extends EvpHashAlgorithmInitializer {
Expr arg;
EvpSignatureDigestInitializer() {
this.(Call).getTarget().getName() in ["EVP_DigestSignInit_ex", "EVP_DigestSignInit"] and
arg = this.(Call).getArgument(2)
or
this.(Call).getTarget().getName() in ["EVP_SignInit", "EVP_SignInit_ex"] and
arg = this.(Call).getArgument(1)
}
override Expr getHashAlgorithmArg() { result = arg }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpSignatureKeyInitializer extends EvpKeyInitializer {
Expr arg;
EvpSignatureKeyInitializer() {
this.(Call).getTarget().getName() = "EVP_DigestSignInit_ex" and
arg = this.(Call).getArgument(5)
or
this.(Call).getTarget().getName() = "EVP_DigestSignInit" and
arg = this.(Call).getArgument(4)
}
override Expr getKeyArg() { result = arg }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class EvpSignaturePrimaryAlgorithmInitializer extends EvpPrimaryAlgorithmInitializer {
Expr arg;
EvpSignaturePrimaryAlgorithmInitializer() {
// signature algorithm
this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] and
arg = this.(Call).getArgument(1)
or
// configuration through the context argument
this.(Call).getTarget().getName() in ["EVP_PKEY_sign_init", "EVP_PKEY_sign_init_ex"] and
arg = this.getContext()
}
override Expr getAlgorithmArg() { result = arg }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
class Evp_Signature_Update_Call extends EvpUpdate {
Evp_Signature_Update_Call() {
this.(Call).getTarget().getName() in [
"EVP_DigestSignUpdate", "EVP_SignUpdate", "EVP_PKEY_sign_message_update"
]
}
/**
* Input is the message to sign.
*/
override Expr getInputArg() { result = this.(Call).getArgument(1) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
}
/**
* We model output explicit output arguments as predicate to use it in constructors.
* The predicate must cover all EVP_Signature_Operation subclasses.
*/
pragma[inline]
private Expr signatureOperationOutputArg(Call call) {
if call.getTarget().getName() = "EVP_SignFinal_ex"
then result = call.getArgument(2)
else result = call.getArgument(1)
}
/**
* The base configuration for all EVP signature operations.
*/
abstract class EvpSignatureOperation extends EvpOperation, Crypto::SignatureOperationInstance {
EvpSignatureOperation() {
this.(Call).getTarget().getName().matches("EVP_%") and
// NULL output argument means the call is to get the size of the signature and such call is not an operation
(
not exists(signatureOperationOutputArg(this).getValue())
or
signatureOperationOutputArg(this).getValue() != "0"
)
}
Expr getHashAlgorithmArg() {
this.getInitCall().(EvpHashAlgorithmInitializer).getHashAlgorithmArg() = result
}
override Expr getAlgorithmArg() {
this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() = result
}
override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() {
AvcToCallArgFlow::flow(result.(OpenSslAlgorithmValueConsumer).getResultNode(),
DataFlow::exprNode(this.getHashAlgorithmArg()))
}
/**
* Signing, verification or unknown.
*/
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
// TODO: if this KeyOperationSubtype does not match initialization call's KeyOperationSubtype then we found a bug
if this.(Call).getTarget().getName().toLowerCase().matches("%sign%")
then result instanceof Crypto::TSignMode
else
if this.(Call).getTarget().getName().toLowerCase().matches("%verify%")
then result instanceof Crypto::TVerifyMode
else result instanceof Crypto::TUnknownKeyOperationMode
}
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
// TODO: some signing operations may have explicit nonce generators
none()
}
/**
* Keys provided in the initialization call or in a context are found by this method.
* Keys in explicit arguments are found by overridden methods in extending classes.
*/
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
result = DataFlow::exprNode(this.getInitCall().(EvpKeyInitializer).getKeyArg())
}
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = EvpOperation.super.getOutputArtifact()
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = EvpOperation.super.getInputConsumer()
}
/**
* TODO: only signing operations for now, change when verificaiton is added
*/
override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { none() }
}
class Evp_Signature_Call extends EvpSignatureOperation {
Evp_Signature_Call() { this.(Call).getTarget().getName() in ["EVP_DigestSign", "EVP_PKEY_sign"] }
/**
* Output is the signature.
*/
override Expr getOutputArg() { result = signatureOperationOutputArg(this) }
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
/**
* Input is the message to sign.
*/
override Expr getInputArg() { result = this.(Call).getArgument(3) }
}
class Evp_Signature_Final_Call extends EvpFinal, EvpSignatureOperation {
Evp_Signature_Final_Call() {
this.(Call).getTarget().getName() in [
"EVP_DigestSignFinal",
"EVP_SignFinal_ex",
"EVP_SignFinal",
"EVP_PKEY_sign_message_final"
]
}
override CtxPointerSource getContext() { result = this.(Call).getArgument(0) }
override Expr getAlgorithmArg() {
this.getInitCall().(EvpPrimaryAlgorithmInitializer).getAlgorithmArg() = result
}
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
// key provided as an argument
this.(Call).getTarget().getName() in ["EVP_SignFinal", "EVP_SignFinal_ex"] and
result = DataFlow::exprNode(this.(Call).getArgument(3))
or
// or find key in the initialization call
result = EvpSignatureOperation.super.getKeyConsumer()
}
/**
* Output is the signature.
*/
override Expr getOutputArg() { result = signatureOperationOutputArg(this) }
}

View File

@@ -1,134 +0,0 @@
/**
* https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis
*/
private import experimental.quantum.Language
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
/**
* A call to and EVP digest initializer, such as:
* - `EVP_DigestInit`
* - `EVP_DigestInit_ex`
* - `EVP_DigestInit_ex2`
*/
class EvpDigestInitVariantCalls extends OperationStep instanceof Call {
EvpDigestInitVariantCalls() {
this.getTarget().getName() in ["EVP_DigestInit", "EVP_DigestInit_ex", "EVP_DigestInit_ex2"]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = PrimaryAlgorithmIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and
type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
/**
* A call to `EVP_DigestUpdate`.
*/
class EvpDigestUpdateCall extends OperationStep instanceof Call {
EvpDigestUpdateCall() { this.getTarget().getName() = "EVP_DigestUpdate" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and
type = ContextIO()
}
override OperationStepType getStepType() { result = UpdateStep() }
}
/**
* A base class for final digest operations.
*/
abstract class EvpFinalDigestOperationStep extends OperationStep {
override OperationStepType getStepType() { result = FinalStep() }
}
/**
* A call to `EVP_Q_digest`
* https://docs.openssl.org/3.0/man3/EVP_DigestInit/#synopsis
*/
class EvpQDigestOperation extends EvpFinalDigestOperationStep instanceof Call {
EvpQDigestOperation() { this.getTarget().getName() = "EVP_Q_digest" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(1) and type = PrimaryAlgorithmIO()
or
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(3) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and
type = ContextIO()
or
result.asDefiningArgument() = this.getArgument(5) and type = DigestIO()
}
}
class EvpDigestOperation extends EvpFinalDigestOperationStep instanceof Call {
EvpDigestOperation() { this.getTarget().getName() = "EVP_Digest" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(4) and type = PrimaryAlgorithmIO()
or
result.asExpr() = this.getArgument(0) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asDefiningArgument() = this.getArgument(2) and type = DigestIO()
}
}
/**
* A call to EVP_DigestFinal variants
*/
class EvpDigestFinalCall extends EvpFinalDigestOperationStep instanceof Call {
EvpDigestFinalCall() {
this.getTarget().getName() in ["EVP_DigestFinal", "EVP_DigestFinal_ex", "EVP_DigestFinalXOF"]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and
type = ContextIO()
or
result.asDefiningArgument() = this.getArgument(1) and type = DigestIO()
}
}
/**
* An openssl digest final hash operation instance
*/
class EvpDigestFinalOperationInstance extends Crypto::HashOperationInstance instanceof EvpFinalDigestOperationStep
{
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
super.getPrimaryAlgorithmValueConsumer() = result
}
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
super.getOutputStepFlowingToStep(DigestIO()).getOutput(DigestIO()) = result
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
super.getDominatingInitializersToStep(PlaintextIO()).getInput(PlaintextIO()) = result
}
}

View File

@@ -1,204 +0,0 @@
private import experimental.quantum.Language
private import OpenSSLOperationBase
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
/**
* A call to EC_KEY_generate_key, which is used to generate an EC key pair.
* Note: this is an operation, though the input parameter is a "EC_KEY*".
* EC_KEY is really an empty context for a key that hasn't been generated, hence
* we consider this an operation generating a key and not accepting a key input.
*/
class ECKeyGen extends OperationStep instanceof Call {
//, Crypto::KeyGenerationOperationInstance {
ECKeyGen() { this.(Call).getTarget().getName() = "EC_KEY_generate_key" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.(Call).getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = KeyIO() }
override OperationStepType getStepType() { result = ContextCreationStep() }
}
/**
* A call to EVP_PKEY_keygen_init or EVP_PKEY_paramgen_init.
*/
class EvpKeyGenInitialize extends OperationStep {
EvpKeyGenInitialize() {
this.getTarget().getName() in [
"EVP_PKEY_keygen_init",
"EVP_PKEY_paramgen_init"
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
abstract class KeyGenFinalOperationStep extends OperationStep {
override OperationStepType getStepType() { result = FinalStep() }
}
/**
* A call to `EVP_PKEY_Q_keygen`
*/
class EvpPKeyQKeyGen extends KeyGenFinalOperationStep instanceof Call {
EvpPKeyQKeyGen() { this.getTarget().getName() = "EVP_PKEY_Q_keygen" }
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this and type = KeyIO()
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
// When arg 3 is a derived type, it is a curve name, otherwise it is a key size for RSA if provided
// and arg 2 is the algorithm type
this.getArgument(3).getType().getUnderlyingType() instanceof DerivedType and
result.asExpr() = this.getArgument(3) and
type = PrimaryAlgorithmIO()
or
not this.getArgument(3).getType().getUnderlyingType() instanceof DerivedType and
result.asExpr() = this.getArgument(2) and
type = PrimaryAlgorithmIO()
or
not this.getArgument(3).getType().getUnderlyingType() instanceof DerivedType and
result.asExpr() = this.getArgument(3) and
type = KeySizeIO()
}
}
/**
* A call to `EVP_RSA_gen`
*/
class EvpRsaGen extends KeyGenFinalOperationStep instanceof Call {
EvpRsaGen() { this.getTarget().getName() = "EVP_RSA_gen" }
override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = KeyIO() }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = KeySizeIO()
}
}
/**
* A call to RSA_generate_key
*/
class RsaGenerateKey extends KeyGenFinalOperationStep instanceof Call {
RsaGenerateKey() { this.getTarget().getName() = "RSA_generate_key" }
override DataFlow::Node getOutput(IOType type) { result.asExpr() = this and type = KeyIO() }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = KeySizeIO()
}
}
/**
* A call to RSA_generate_key_ex
*/
class RsaGenerateKeyEx extends KeyGenFinalOperationStep instanceof Call {
RsaGenerateKeyEx() { this.getTarget().getName() = "RSA_generate_key_ex" }
override DataFlow::Node getOutput(IOType type) {
result.asDefiningArgument() = this.getArgument(0) and type = KeyIO()
}
override DataFlow::Node getInput(IOType type) {
// arg 0 comes in as a blank RSA key, which we consider a context,
// on output it is considered a key
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
}
/**
* A call to `EVP_PKEY_generate` or `EVP_PKEY_keygen`.
*/
class EvpPkeyGen extends KeyGenFinalOperationStep instanceof Call {
EvpPkeyGen() { this.getTarget().getName() in ["EVP_PKEY_generate", "EVP_PKEY_keygen"] }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asDefiningArgument() = this.getArgument(1) and type = KeyIO()
or
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
}
/**
* A call to `EVP_PKEY_new_mac_key` that creates a new generic MAC key.
* - EVP_PKEY *EVP_PKEY_new_mac_key(int type, ENGINE *e, const unsigned char *key, int keylen);
*/
class EvpNewMacKey extends KeyGenFinalOperationStep {
EvpNewMacKey() { this.getTarget().getName() = "EVP_PKEY_new_mac_key" }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
// the raw key that is configured into the output key
result.asExpr() = this.getArgument(2) and type = KeyIO()
or
result.asExpr() = this.getArgument(3) and type = KeySizeIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this and type = KeyIO()
or
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
}
/// TODO: https://docs.openssl.org/3.0/man3/EVP_PKEY_new/#synopsis
/**
* An `KeyGenerationOperationInstance` for the for all key gen final operation steps.
*/
class KeyGenOperationInstance extends Crypto::KeyGenerationOperationInstance instanceof KeyGenFinalOperationStep
{
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
super.getPrimaryAlgorithmValueConsumer() = result
}
override Crypto::KeyArtifactType getOutputKeyType() { result = Crypto::TAsymmetricKeyType() }
override Crypto::ArtifactOutputDataFlowNode getOutputKeyArtifact() {
super.getOutputStepFlowingToStep(KeyIO()).getOutput(KeyIO()) = result
}
override predicate hasKeyValueConsumer() {
exists(OperationStep s | s.flowsToOperationStep(this) and s.setsValue(KeyIO()))
}
override Crypto::ConsumerInputDataFlowNode getKeySizeConsumer() {
super.getDominatingInitializersToStep(KeySizeIO()).getInput(KeySizeIO()) = result
}
override int getKeySizeFixed() {
none()
// TODO: marked as none as the operation itself has no key size, it
// comes from the algorithm source, but note we could grab the
// algorithm source and get the key size (see below).
// We may need to reconsider what is the best approach here.
// result =
// this.getAnAlgorithmValueConsumer()
// .getAKnownAlgorithmSource()
// .(Crypto::EllipticCurveInstance)
// .getKeySize()
}
override Crypto::ConsumerInputDataFlowNode getKeyValueConsumer() {
super.getDominatingInitializersToStep(KeyIO()).getInput(KeyIO()) = result
}
}

View File

@@ -1,523 +1,316 @@
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.AvcFlow
private import experimental.quantum.OpenSSL.CtxFlow
private import experimental.quantum.OpenSSL.KeyFlow
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
import semmle.code.cpp.dataflow.new.DataFlow
// Importing these intializers here to ensure the are part of any model that is
// using OpenSslOperationBase. This further ensures that initializers are tied to opeartions
// even if only importing the operation by itself.
import EVPPKeyCtxInitializer
/**
* An openSSL CTX type, which is type for which the stripped underlying type
* matches the pattern 'evp_%ctx_%st'.
* This includes types like:
* - EVP_CIPHER_CTX
* - EVP_MD_CTX
* - EVP_PKEY_CTX
*/
class CtxType extends Type {
CtxType() {
// It is possible for users to use the underlying type of the CTX variables
// these have a name matching 'evp_%ctx_%st
this.getUnspecifiedType().stripType().getName().matches("evp_%ctx_%st")
or
// In principal the above check should be sufficient, but in case of build mode none issues
// i.e., if a typedef cannot be resolved,
// or issues with properly stubbing test cases, we also explicitly check for the wrapping type defs
// i.e., patterns matching 'EVP_%_CTX'
exists(Type base | base = this or base = this.(DerivedType).getBaseType() |
base.getName().matches("EVP_%_CTX")
)
}
}
/**
* A pointer to a CtxType
*/
class CtxPointerExpr extends Expr {
CtxPointerExpr() {
this.getType() instanceof CtxType and
this.getType() instanceof PointerType
}
}
/**
* A call argument of type CtxPointerExpr.
*/
class CtxPointerArgument extends CtxPointerExpr {
CtxPointerArgument() { exists(Call c | c.getAnArgument() = this) }
Call getCall() { result.getAnArgument() = this }
}
/**
* The type of inputs and ouputs for an `OperationStep`.
*/
newtype TIOType =
CiphertextIO() or
// Used for typical CTX types, but not for OSSL_PARAM or OSSL_LIB_CTX
// For OSSL_PARAM and OSSL_LIB_CTX use of OsslParamIO and OsslLibContextIO
ContextIO() or
DigestIO() or
HashAlgorithmIO() or
IVorNonceIO() or
KeyIO() or
KeyOperationSubtypeIO() or
KeySizeIO() or
// Used for OSSL_LIB_CTX
OsslLibContextIO() or
// Used for OSSL_PARAM
OsslParamIO() or
MacIO() or
PaddingAlgorithmIO() or
// Plaintext also includes a message for digest, signature, verification, and mac generation
PlaintextIO() or
PrimaryAlgorithmIO() or
RandomSourceIO() or
SaltLengthIO() or
SeedIO() or
SignatureIO()
private string ioTypeToString(TIOType t) {
t = CiphertextIO() and result = "CiphertextIO"
or
t = ContextIO() and result = "ContextIO"
or
t = DigestIO() and result = "DigestIO"
or
t = HashAlgorithmIO() and result = "HashAlgorithmIO"
or
t = IVorNonceIO() and result = "IVorNonceIO"
or
t = KeyIO() and result = "KeyIO"
or
t = KeyOperationSubtypeIO() and result = "KeyOperationSubtypeIO"
or
t = KeySizeIO() and result = "KeySizeIO"
or
t = OsslLibContextIO() and result = "OsslLibContextIO"
or
t = OsslParamIO() and result = "OsslParamIO"
or
t = MacIO() and result = "MacIO"
or
t = PaddingAlgorithmIO() and result = "PaddingAlgorithmIO"
or
t = PlaintextIO() and result = "PlaintextIO"
or
t = PrimaryAlgorithmIO() and result = "PrimaryAlgorithmIO"
or
t = RandomSourceIO() and result = "RandomSourceIO"
or
t = SaltLengthIO() and result = "SaltLengthIO"
or
t = SeedIO() and result = "SeedIO"
or
t = SignatureIO() and result = "SignatureIO"
}
class IOType extends TIOType {
string toString() {
result = ioTypeToString(this)
or
not exists(ioTypeToString(this)) and result = "UnknownIOType"
}
}
//TODO: add more initializers as needed
/**
* The type of step in an `OperationStep`.
* - `ContextCreationStep`: the creation of a context from an algorithm or key.
* for example `EVP_MD_CTX_create(EVP_sha256())` or `EVP_PKEY_CTX_new(pkey, NULL)`
* - `InitializerStep`: the initialization of an operation through some sort of shared/accumulated context
* for example `EVP_DigestInit_ex(ctx, EVP_sha256(), NULL)`
* - `UpdateStep`: any operation that has and update/final paradigm, the update represents an intermediate step in an operation,
* such as `EVP_DigestUpdate(ctx, data, len)`
* - `FinalStep`: an ultimate operation step. This may be an explicit 'final' in an update/final paradigm, but not necessarily.
* Any operation that does nto operate through an update/final paradigm is considered a final step.
*/
newtype OperationStepType =
// Context creation captures cases where a context is created from an algorithm or key
//
ContextCreationStep() or
InitializerStep() or
UpdateStep() or
FinalStep()
/**
* A step in configuring an operation.
* Captures creation of contexts from algorithms or keys,
* initalization of configurations on contexts,
* update operations (intermediate steps in an operation)
* and the operation itself.
*
* NOTE: if an operation is configured through a means other than a call
* e.g., a pattern like ctx->alg = EVP_sha256()
* then this class will need to be modified to account for that paradigm.
* Currently, this is not a known pattern in OpenSSL.
*/
abstract class OperationStep extends Call {
/**
* Gets the output nodes from the given operation step.
* These are the nodes that flow connecting this step
* to any other step in the operation should follow.
*/
abstract DataFlow::Node getOutput(IOType type);
/**
* Gets any output node from the given operation step.
*/
final DataFlow::Node getAnOutput() { result = this.getOutput(_) }
/**
* Gets the input nodes for the given operation step.
*/
abstract DataFlow::Node getInput(IOType type);
/**
* Gets any input node for the given operation step.
*/
final DataFlow::Node getAnInput() { result = this.getInput(_) }
/**
* Gets the type of the step, e.g., ContextCreationStep, InitializerStep, UpdateStep, FinalStep.
*/
abstract OperationStepType getStepType();
/**
* Holds if this operation step flows to the given `OperationStep` `sink`.
* If `sink` is `this`, then this holds true.
*/
predicate flowsToOperationStep(OperationStep sink) {
sink = this or
OperationStepFlow::flow(this.getAnOutput(), sink.getAnInput())
}
/**
* Holds if this operation step flows from the given `OperationStep` (`source`).
* If `source` is `this`, then this holds true.
*/
predicate flowsFromOperationStep(OperationStep source) {
source = this or
OperationStepFlow::flow(source.getAnOutput(), this.getAnInput())
}
/**
* Holds if this operation step sets a value of the given `IOType`.
*/
predicate setsValue(IOType type) { exists(this.getInput(type)) }
/**
* Gets operation steps that flow to `this` and set the given `IOType`.
* This checks for the last initializers that flow to the `this`,
* i.e., if a value is set then re-set, the last set operation step is returned,
* not both.
* Note: Any 'update' that sets a value is not considered to be 'resetting' an input.
* I.e., there is a difference between changing a configuration before use and
* the operation allows for multiple inputs (like plaintext for cipher update calls before final).
*/
OperationStep getDominatingInitializersToStep(IOType type) {
result.flowsToOperationStep(this) and
result.setsValue(type) and
(
// Do not consider a 'reset' to occur on updates
result.getStepType() = UpdateStep()
or
not exists(OperationStep reset |
result != reset and
reset.setsValue(type) and
reset.flowsToOperationStep(this) and
result.flowsToOperationStep(reset)
)
)
}
/**
* Gets all output of `type` that flow to `this`
* if `this` is a final step and the output is not from
* a separate final step.
*/
OperationStep getOutputStepFlowingToStep(IOType type) {
this.getStepType() = FinalStep() and
result.flowsToOperationStep(this) and
exists(result.getOutput(type)) and
(result = this or result.getStepType() != FinalStep())
}
/**
* Gets an AVC for the primary algorithm for this operation.
* A primary algorithm is an AVC that flows to a ctx input directly or
* an AVC that flows to a primary algorithm input directly.
* See `AvcContextCreationStep` for details about resetting scenarios.
* Gets the first OperationStep an AVC flows to. If a context input,
* the AVC is considered primary.
* If a primary algorithm input, then get the last set primary algorithm
* operation step (dominating operation step, see `getDominatingInitializersToStep`).
*/
Crypto::AlgorithmValueConsumer getPrimaryAlgorithmValueConsumer() {
exists(DataFlow::Node src, DataFlow::Node sink, IOType t, OperationStep avcSucc |
(t = PrimaryAlgorithmIO() or t = ContextIO()) and
avcSucc.flowsToOperationStep(this) and
src.asExpr() = result and
sink = avcSucc.getInput(t) and
AvcToOperationStepFlow::flow(src, sink) and
(
// Case 1: the avcSucc step is a dominating initialization step
t = PrimaryAlgorithmIO() and
avcSucc = this.getDominatingInitializersToStep(PrimaryAlgorithmIO())
or
// Case 2: the succ is a context input (any avcSucc is valid)
t = ContextIO()
)
)
}
/**
* Gets the algorithm value consumer for an input to `this` operation step
* of the given `type`.
* TODO: generalize to use this for `getPrimaryAlgorithmValueConsumer`
*/
Crypto::AlgorithmValueConsumer getAlgorithmValueConsumerForInput(IOType type) {
exists(DataFlow::Node src, DataFlow::Node sink |
AvcToOperationStepFlow::flow(src, sink) and
src.asExpr() = result and
sink = this.getInput(type)
)
}
}
/**
* An AVC is considered to output a 'context type', however,
* each AVC has it's own output types in practice.
* Some output algorithm containers (`EVP_get_cipherbyname`)
* some output explicit contexts (`EVP_PKEY_CTX_new_from_name`).
* The output of an AVC cannot be determined to be a primary algorithm (PrimaryAlgorithmIO), that depends
* on the use of the AVC output.
* The use is assumed to be of two forms:
* - The AVC output flows to a known input that accepts an algorithm
* e.g., `EVP_DigestInit(ctx, type)` the `type` parameter is known to be the primary algorithm.
* `EVP_SignInit(ctx, type)` the `type` parameter is known to be a digest algorithm for the signature.
* - The AVC output flows to a context initialization step
* e.g., `pkey_ctx = EVP_PKEY_CTX_new_from_name(libctx, name, propquery)` this is an AVC call, but the
* API says the output is a context. It is consumed typically by something like:
* `ctx = EVP_PKEY_keygen_init(pkey_ctx)`, but note I cannot consider the `pkey_ctx` parameter to always be a primary algorithm,
* a key gen can be inited by a prior key as well, e.g., `ctx = EVP_PKEY_CTX_new(pkey, NULL)`.
* Hence, these initialization steps take in a context that may have come from an AVC or something else,
* and therefore cannot be considered a primary algorithm.
* Assumption: The first operation step an AVC flows to will be of the above two forms.
* Resetting Algorithm Concerns and Assumptions:
* What if a user resets the algorithm through another AVC call?
* How would we detect that and only look at the 'dominating' (last set) AVC?
* From an AVC, always assess the first operation step it flows to.
* If the first step is to a context input, then we assume that reset is not possible in the same path.
* I.e., a user cannot reset the algorithm without starting an entirely new operation step chain.
* See the use patterns for `pkey_ctx = EVP_PKEY_CTX_new_from_name(...)` mentioned above. A user cannot
* reset the algorithm without calling a new `ctx = EVP_PKEY_keygen_init(pkey_ctx)`,
* i.e., subsequent flow follows the `ctx` output.
* If the first step is to any other input, then we use the `getDominatingInitializersToStep`
* to find the last AVC that set the algorithm for the operation step.
* Domination checks must occur at an operation step (e.g., at a final operation).
* This operation step does not find the dominating AVC.
* If a primary algorithm is explicitly set and and AVC is set through a context input,
* we will use both cases as primary inputs.
*/
class AvcContextCreationStep extends OperationStep instanceof OpenSslAlgorithmValueConsumer {
override DataFlow::Node getOutput(IOType type) {
type = ContextIO() and result = super.getResultNode()
}
override DataFlow::Node getInput(IOType type) { none() }
override OperationStepType getStepType() { result = ContextCreationStep() }
}
abstract private class CtxPassThroughCall extends Call {
abstract DataFlow::Node getNode1();
abstract DataFlow::Node getNode2();
}
/**
* A call whose target contains 'free' or 'reset' and has an argument of type
* CtxPointerArgument.
*/
private class CtxClearCall extends Call {
CtxClearCall() {
this.getTarget().getName().toLowerCase().matches(["%free%", "%reset%"]) and
this.getAnArgument() instanceof CtxPointerArgument
}
}
/**
* A call whose target contains 'copy' and has an argument of type
* CtxPointerArgument.
*/
private class CtxCopyOutArgCall extends CtxPassThroughCall {
DataFlow::Node n1;
DataFlow::Node n2;
CtxCopyOutArgCall() {
this.getTarget().getName().toLowerCase().matches("%copy%") and
n1.asExpr() = this.getAnArgument() and
n1.getType() instanceof CtxType and
n2.asDefiningArgument() = this.getAnArgument() and
n2.getType() instanceof CtxType and
n1.asDefiningArgument() != n2.asExpr()
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
/**
* A call whose target contains 'dup' and has an argument of type
* CtxPointerArgument.
*/
private class CtxCopyReturnCall extends CtxPassThroughCall, CtxPointerExpr {
DataFlow::Node n1;
CtxCopyReturnCall() {
this.getTarget().getName().toLowerCase().matches("%dup%") and
n1.asExpr() = this.getAnArgument() and
n1.getType() instanceof CtxType
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result.asExpr() = this }
}
// TODO: is this still needed?
/**
* A call to `EVP_PKEY_paramgen` acts as a kind of pass through.
* It's output pkey is eventually used in a new operation generating
* a fresh context pointer (e.g., `EVP_PKEY_CTX_new`).
* It is easier to model this as a pass through
* than to model the flow from the paramgen to the new key generation.
*/
private class CtxParamGenCall extends CtxPassThroughCall {
DataFlow::Node n1;
DataFlow::Node n2;
CtxParamGenCall() {
this.getTarget().getName() = "EVP_PKEY_paramgen" and
n1.asExpr() = this.getArgument(0) and
(
n2.asExpr() = this.getArgument(1)
or
n2.asDefiningArgument() = this.getArgument(1)
)
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
//TODO: I am not sure CallArgToCtxRet is needed anymore
/**
* If the current node is an argument to a function
* that returns a pointer type, immediately flow through.
* NOTE: this passthrough is required if we allow
* intermediate steps to go into variables that are not a CTX type.
* See for example `CtxParamGenCall`.
*/
private class CallArgToCtxRet extends CtxPassThroughCall, CtxPointerExpr {
DataFlow::Node n1;
DataFlow::Node n2;
CallArgToCtxRet() {
this.getAnArgument() = n1.asExpr() and
n2.asExpr() = this
}
override DataFlow::Node getNode1() { result = n1 }
override DataFlow::Node getNode2() { result = n2 }
}
/**
* A flow configuration from any non-final `OperationStep` to any other `OperationStep`.
*/
module OperationStepFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(OperationStep s |
s.getAnOutput() = source or
s.getAnInput() = source
)
}
predicate isSink(DataFlow::Node sink) {
exists(OperationStep s |
s.getAnInput() = sink or
s.getAnOutput() = sink
)
}
predicate isBarrier(DataFlow::Node node) {
exists(CtxClearCall c | c.getAnArgument() = node.asExpr())
}
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2)
or
// Flow out through all outputs from an operation step if more than one output
// is defined.
exists(OperationStep s | s.getAnInput() = node1 and s.getAnOutput() = node2)
// TODO: consideration for additional alises defined as follows:
// if an output from an operation step itself flows from the output of another operation step
// then the source of that flow's outputs (all of them) are potential aliases
}
}
module OperationStepFlow = DataFlow::Global<OperationStepFlowConfig>;
/**
* A flow from AVC to the first `OperationStep` the AVC reaches as an input.
*/
module AvcToOperationStepFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
exists(AvcContextCreationStep s | s.getAnOutput() = source)
}
predicate isSink(DataFlow::Node sink) { exists(OperationStep s | s.getAnInput() = sink) }
predicate isBarrier(DataFlow::Node node) {
exists(CtxClearCall c | c.getAnArgument() = node.asExpr())
}
/**
* Only get the first operation step encountered.
*/
predicate isBarrierOut(DataFlow::Node node) { isSink(node) }
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(CtxPassThroughCall c | c.getNode1() = node1 and c.getNode2() = node2)
}
}
module AvcToOperationStepFlow = DataFlow::Global<AvcToOperationStepFlowConfig>;
module EncValToInitEncArgConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source.asExpr().getValue().toInt() in [0, 1] }
predicate isSink(DataFlow::Node sink) {
exists(OperationStep s | sink = s.getInput(KeyOperationSubtypeIO()))
exists(EvpKeyOperationSubtypeInitializer initCall |
sink.asExpr() = initCall.getKeyOperationSubtypeArg()
)
}
}
module EncValToInitEncArgFlow = DataFlow::Global<EncValToInitEncArgConfig>;
private Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) {
i = 0 and
result instanceof Crypto::TEncryptMode
private predicate argToAvc(Expr arg, Crypto::AlgorithmValueConsumer avc) {
// NOTE: because we trace through keys to their sources we must consider that the arg is an avc
// Consider this example:
// EVP_PKEY *pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, key_len);
// The key may trace into a signing operation. Tracing through the key we will get the arg taking `EVP_PKEY_HMAC`
// as the algorithm value consumer (the input node of the AVC). The output node of this AVC
// is the call return of `EVP_PKEY_new_mac_key`. If we trace from the AVC result to
// the input argument this will not be possible (from the return to the call argument is a backwards flow).
// Therefore, we must consider the input node of the AVC as the argument.
// This should only occur due to tracing through keys to find configuration data.
avc.getInputNode().asExpr() = arg
or
i = 1 and result instanceof Crypto::TDecryptMode
AvcToCallArgFlow::flow(avc.(OpenSslAlgorithmValueConsumer).getResultNode(),
DataFlow::exprNode(arg))
}
Crypto::KeyOperationSubtype resolveKeyOperationSubTypeOperationStep(OperationStep s) {
exists(DataFlow::Node src |
EncValToInitEncArgFlow::flow(src, s.getInput(KeyOperationSubtypeIO())) and
result = intToCipherOperationSubtype(src.asExpr().getValue().toInt())
)
/**
* A class for all OpenSsl operations.
*/
abstract class OpenSslOperation extends Crypto::OperationInstance instanceof Call {
/**
* Gets the argument that specifies the algorithm for the operation.
* This argument might not be immediately present at the specified operation.
* For example, it might be set in an initialization call.
* Modelers of the operation are resonsible for linking the operation to any
* initialization calls, and providing that argument as a returned value here.
*/
abstract Expr getAlgorithmArg();
/**
* Algorithm is specified in initialization call or is implicitly established by the key.
*/
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
argToAvc(this.getAlgorithmArg(), result)
}
}
/**
* A Call to an initialization function for an operation.
* These are not operations in the sense of Crypto::OperationInstance,
* but they are used to initialize the context for the operation.
* There may be multiple initialization calls for the same operation.
* Intended for use with EvPOperation.
*/
abstract class EvpInitializer extends Call {
/**
* Gets the context argument or return that ties together initialization, updates and/or final calls.
* The context is the context coming into the initializer and is the output as well.
* This is assumed to be the same argument.
*/
abstract CtxPointerSource getContext();
}
/**
* A call to initialize a key size.
*/
abstract class EvpKeySizeInitializer extends EvpInitializer {
abstract Expr getKeySizeArg();
}
/**
* A call to initialize a key operation subtype.
*/
abstract class EvpKeyOperationSubtypeInitializer extends EvpInitializer {
abstract Expr getKeyOperationSubtypeArg();
private Crypto::KeyOperationSubtype intToCipherOperationSubtype(int i) {
i = 0 and
result instanceof Crypto::TEncryptMode
or
i = 1 and result instanceof Crypto::TDecryptMode
}
Crypto::KeyOperationSubtype getKeyOperationSubtype() {
exists(DataFlow::Node a, DataFlow::Node b |
EncValToInitEncArgFlow::flow(a, b) and
b.asExpr() = this.getKeyOperationSubtypeArg() and
result = this.intToCipherOperationSubtype(a.asExpr().getValue().toInt())
)
or
// Infer the subtype from the initialization call, and ignore the argument
this.(Call).getTarget().getName().toLowerCase().matches("%encrypt%") and
result instanceof Crypto::TEncryptMode
or
this.(Call).getTarget().getName().toLowerCase().matches("%decrypt%") and
result instanceof Crypto::TDecryptMode
}
}
/**
* An primary algorithm initializer initializes the primary algorithm for a given operation.
* For example, for a signing operation, the algorithm initializer may initialize algorithms
* like RSA. Other algorithsm may be initialized on an operation, as part of a larger
* operation/protocol. For example, hashing operations on signing operations; however,
* these are not the primary algorithm. Any other algorithms initialized on an operation
* require a specialized initializer, such as EvpHashAlgorithmInitializer.
*/
abstract class EvpPrimaryAlgorithmInitializer extends EvpInitializer {
abstract Expr getAlgorithmArg();
Crypto::AlgorithmValueConsumer getAlgorithmValueConsumer() {
argToAvc(this.getAlgorithmArg(), result)
}
}
/**
* A call to initialize a key.
*/
abstract class EvpKeyInitializer extends EvpInitializer {
abstract Expr getKeyArg();
}
/**
* A key initializer may initialize the algorithm and the key size through
* the key. Extend any instance of key initializer provide initialization
* of the algorithm and key size from the key.
*/
class EvpInitializerThroughKey extends EvpPrimaryAlgorithmInitializer, EvpKeySizeInitializer,
EvpKeyInitializer
{
Expr arg;
CtxPointerSource context;
EvpInitializerThroughKey() {
exists(EvpKeyInitializer keyInit |
arg = keyInit.getKeyArg() and this = keyInit and context = keyInit.getContext()
)
}
override CtxPointerSource getContext() { result = context }
override Expr getAlgorithmArg() {
result =
getSourceKeyCreationInstanceFromArg(this.getKeyArg()).(OpenSslOperation).getAlgorithmArg()
}
override Expr getKeySizeArg() {
result = getSourceKeyCreationInstanceFromArg(this.getKeyArg()).getKeySizeConsumer().asExpr()
}
override Expr getKeyArg() { result = arg }
}
/**
* A default initializer for any key operation that accepts a key as input.
* A key initializer allows for a mechanic to go backwards to the key creation operation
* and find the algorithm and key size.
* If a user were to stipualte a key consumer for an operation but fail to indicate it as an
* initializer, automatic tracing to the creation operation would not occur.
* USERS SHOULD NOT NEED TO USE OR EXTEND THIS CLASS DIRECTLY.
*
* TODO: re-evaluate this approach
*/
class DefaultKeyInitializer extends EvpKeyInitializer instanceof Crypto::KeyOperationInstance {
Expr arg;
DefaultKeyInitializer() {
exists(Call c |
c.getAChild*() = arg and
arg = this.(Crypto::KeyOperationInstance).getKeyConsumer().asExpr() and
c = this
)
}
override Expr getKeyArg() { result = arg }
override CtxPointerSource getContext() { result = this.(EvpOperation).getContext() }
}
abstract class EvpIVInitializer extends EvpInitializer {
abstract Expr getIVArg();
}
/**
* A call to initialize padding.
*/
abstract class EvpPaddingInitializer extends EvpInitializer {
/**
* Gets the padding mode argument.
* e.g., `EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING)` argument 1 (0-based)
*/
abstract Expr getPaddingArg();
}
/**
* A call to initialize a salt length.
*/
abstract class EvpSaltLengthInitializer extends EvpInitializer {
/**
* Gets the salt length argument.
* e.g., `EVP_PKEY_CTX_set_scrypt_salt_len(ctx, 16)` argument 1 (0-based)
*/
abstract Expr getSaltLengthArg();
}
/**
* A call to initialize a hash algorithm.
*/
abstract class EvpHashAlgorithmInitializer extends EvpInitializer {
abstract Expr getHashAlgorithmArg();
Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() {
argToAvc(this.getHashAlgorithmArg(), result)
}
}
/**
* A Call to an "update" function.
* These are not operations in the sense of Crypto::OperationInstance,
* but produce intermediate results for the operation that are later finalized
* (see EvpFinal).
* Intended for use with EvPOperation.
*/
abstract class EvpUpdate extends Call {
/**
* Gets the context argument that ties together initialization, updates and/or final calls.
*/
abstract CtxPointerSource getContext();
/**
* Update calls always have some input data like plaintext or message digest.
*/
abstract Expr getInputArg();
/**
* Update calls sometimes have some output data like a plaintext.
*/
Expr getOutputArg() { none() }
}
/**
* The base class for all operations of the EVP API.
* This captures one-shot APIs (with and without an initilizer call) and final calls.
* Provides some default methods for Crypto::KeyOperationInstance class.
*/
abstract class EvpOperation extends OpenSslOperation {
/**
* Gets the context argument that ties together initialization, updates and/or final calls.
*/
abstract CtxPointerSource getContext();
/**
* Some input data like plaintext or message digest.
* Either argument provided direcly in the call or all arguments that were provided in update calls.
*/
abstract Expr getInputArg();
/**
* Some output data like ciphertext or signature.
*/
abstract Expr getOutputArg();
/**
* Finds the initialization call, may be none.
*/
EvpInitializer getInitCall() { ctxSrcToSrcFlow(result.getContext(), this.getContext()) }
Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
result = DataFlow::exprNode(this.getOutputArg())
}
/**
* Input consumer is the input argument of the call.
*/
Crypto::ConsumerInputDataFlowNode getInputConsumer() {
result = DataFlow::exprNode(this.getInputArg())
}
}
/**
* An EVP final call,
* which is typicall used in an update/final pattern.
* Final operations are typically identified by "final" in the name,
* e.g., "EVP_DigestFinal", "EVP_EncryptFinal", etc.
* however, this is not a strict rule.
*/
abstract class EvpFinal extends EvpOperation {
/**
* All update calls that were executed before this final call.
*/
EvpUpdate getUpdateCalls() { ctxSrcToSrcFlow(result.getContext(), this.getContext()) }
/**
* Gets the input data provided to all update calls.
* If more input data was provided in the final call, override the method.
*/
override Expr getInputArg() { result = this.getUpdateCalls().getInputArg() }
/**
* Gets the output data provided to all update calls.
* If more output data was provided in the final call, override the method.
*/
override Expr getOutputArg() { result = this.getUpdateCalls().getOutputArg() }
}

View File

@@ -1,5 +1,6 @@
import OpenSSLOperationBase
import CipherOperation
import HashOperation
import SignatureOperation
import KeyGenOperation
import EVPCipherOperation
import EVPHashOperation
import ECKeyGenOperation
import EVPSignatureOperation
import EVPKeyGenOperation

View File

@@ -1,260 +0,0 @@
/**
* Provides classes for modeling OpenSSL's EVP signature operations
*/
private import experimental.quantum.Language
private import experimental.quantum.OpenSSL.AvcFlow
private import experimental.quantum.OpenSSL.AlgorithmValueConsumers.OpenSSLAlgorithmValueConsumers
private import experimental.quantum.OpenSSL.Operations.OpenSSLOperations
// TODO: verification functions
/**
* A base class for final signature operations.
*/
abstract class EvpSignatureFinalOperation extends OperationStep {
override OperationStepType getStepType() { result = FinalStep() }
}
/**
* A call to EVP_DigestSignInit or EVP_DigestSignInit_ex.
*/
class EvpSignatureDigestInitializer extends OperationStep {
EvpSignatureDigestInitializer() {
this.getTarget().getName() in ["EVP_DigestSignInit_ex", "EVP_DigestSignInit"]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
this.getTarget().getName() = "EVP_DigestSignInit_ex" and
result.asExpr() = this.getArgument(3) and
type = OsslLibContextIO()
or
result.asExpr() = this.getArgument(2) and type = HashAlgorithmIO()
or
this.getTarget().getName() = "EVP_DigestSignInit" and
result.asExpr() = this.getArgument(4) and
type = KeyIO()
or
this.getTarget().getName() = "EVP_DigestSignInit_ex" and
result.asExpr() = this.getArgument(5) and
type = KeyIO()
or
this.getTarget().getName() = "EVP_DigestSignInit_ex" and
result.asExpr() = this.getArgument(6) and
type = OsslParamIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
// EVP_PKEY_CTX
result.asExpr() = this.getArgument(1) and type = ContextIO()
or
this.getTarget().getName() = "EVP_DigestSignInit_ex" and
result.asExpr() = this.getArgument(6) and
type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
/**
* A call to EVP_SignInit or EVP_SignInit_ex.
*/
class EvpSignInit extends OperationStep {
EvpSignInit() { this.getTarget().getName() in ["EVP_SignInit", "EVP_SignInit_ex"] }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = HashAlgorithmIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
/**
* A call to:
* - EVP_PKEY_sign_init_ex
* - EVP_PKEY_sign_init_ex2
* - EVP_PKEY_sign_init
* - EVP_PKEY_sign_message_init
*/
class EvpPkeySignInit extends OperationStep {
EvpPkeySignInit() {
this.getTarget().getName() in [
"EVP_PKEY_sign_init_ex", "EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_init",
"EVP_PKEY_sign_message_init"
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
this.getTarget().getName() in ["EVP_PKEY_sign_init_ex2", "EVP_PKEY_sign_message_init"] and
result.asExpr() = this.getArgument(1) and
type = PrimaryAlgorithmIO()
or
this.getTarget().getName() = "EVP_PKEY_sign_init_ex" and
result.asExpr() = this.getArgument(1) and
type = OsslParamIO()
or
// Argument 2 (0 based) only exists for EVP_PKEY_sign_init_ex2 and EVP_PKEY_sign_message_init
result.asExpr() = this.getArgument(2) and type = OsslParamIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = InitializerStep() }
}
/**
* A call to EVP_DIgestSignUpdate, EVP_SignUpdate or EVP_PKEY_sign_message_update.
*/
class EvpSignatureUpdateCall extends OperationStep {
EvpSignatureUpdateCall() {
this.getTarget().getName() in [
"EVP_DigestSignUpdate", "EVP_SignUpdate", "EVP_PKEY_sign_message_update"
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override OperationStepType getStepType() { result = UpdateStep() }
}
/**
* A call to EVP_SignFinal or EVP_SignFinal_ex.
*/
class EvpSignFinal extends EvpSignatureFinalOperation {
EvpSignFinal() { this.getTarget().getName() in ["EVP_SignFinal_ex", "EVP_SignFinal"] }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(3) and type = KeyIO()
or
// params above 3 (0-based) only exist for EVP_SignFinal_ex
result.asExpr() = this.getArgument(4) and
type = OsslLibContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = SignatureIO()
}
}
/**
* A call to EVP_DigestSign or EVP_PKEY_sign.
*/
class EvpDigestSign extends EvpSignatureFinalOperation {
EvpDigestSign() { this.getTarget().getName() in ["EVP_DigestSign", "EVP_PKEY_sign"] }
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(3) and type = PlaintextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = SignatureIO()
}
}
/**
* A call to EVP_DigestSignFinal or EVP_PKEY_sign_message_final.
*/
class EvpDigestAndPkeySignFinal extends EvpSignatureFinalOperation {
EvpDigestAndPkeySignFinal() {
this.getTarget().getName() in [
"EVP_DigestSignFinal",
"EVP_PKEY_sign_message_final"
]
}
override DataFlow::Node getInput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
}
override DataFlow::Node getOutput(IOType type) {
result.asExpr() = this.getArgument(0) and type = ContextIO()
or
result.asExpr() = this.getArgument(1) and type = SignatureIO()
}
override OperationStepType getStepType() { result = FinalStep() }
}
/**
* An EVP signature operation instance.
*/
class EvpSignatureOperationInstance extends Crypto::SignatureOperationInstance instanceof EvpSignatureFinalOperation
{
override Crypto::AlgorithmValueConsumer getAnAlgorithmValueConsumer() {
super.getPrimaryAlgorithmValueConsumer() = result
}
/**
* Signing, verification or unknown.
*/
override Crypto::KeyOperationSubtype getKeyOperationSubtype() {
// TODO: if this KeyOperationSubtype does not match initialization call's KeyOperationSubtype then we found a bug
if super.getTarget().getName().toLowerCase().matches("%sign%")
then result instanceof Crypto::TSignMode
else
if super.getTarget().getName().toLowerCase().matches("%verify%")
then result instanceof Crypto::TVerifyMode
else result instanceof Crypto::TUnknownKeyOperationMode
}
override Crypto::ConsumerInputDataFlowNode getNonceConsumer() {
// TODO: some signing operations may have explicit nonce generators
none()
}
/**
* Keys provided in the initialization call or in a context are found by this method.
* Keys in explicit arguments are found by overridden methods in extending classes.
*/
override Crypto::ConsumerInputDataFlowNode getKeyConsumer() {
super.getDominatingInitializersToStep(KeyIO()).getInput(KeyIO()) = result
}
override Crypto::ArtifactOutputDataFlowNode getOutputArtifact() {
super.getOutputStepFlowingToStep(SignatureIO()).getOutput(SignatureIO()) = result
}
override Crypto::ConsumerInputDataFlowNode getInputConsumer() {
super.getDominatingInitializersToStep(PlaintextIO()).getInput(PlaintextIO()) = result
}
/**
* TODO: only signing operations for now, change when verificaiton is added
*/
override Crypto::ConsumerInputDataFlowNode getSignatureConsumer() { none() }
override Crypto::AlgorithmValueConsumer getHashAlgorithmValueConsumer() {
super
.getDominatingInitializersToStep(HashAlgorithmIO())
.getAlgorithmValueConsumerForInput(HashAlgorithmIO()) = result
}
}

View File

@@ -32,8 +32,4 @@ extensions:
- ["", "", False, "CommandLineToArgvA", "", "", "Argument[*0]", "ReturnValue[**]", "taint", "manual"]
- ["", "", False, "CommandLineToArgvW", "", "", "Argument[*0]", "ReturnValue[**]", "taint", "manual"]
# fileapi.h
- ["", "", False, "ReadFileEx", "", "", "Argument[*3].Field[@hEvent]", "Argument[4].Parameter[*2].Field[@hEvent]", "value", "manual"]
# processthreadsapi.h
- ["", "", False, "CreateThread", "", "", "Argument[@3]", "Argument[2].Parameter[@0]", "value", "manual"]
- ["", "", False, "CreateRemoteThread", "", "", "Argument[@4]", "Argument[3].Parameter[@0]", "value", "manual"]
- ["", "", False, "CreateRemoteThreadEx", "", "", "Argument[@4]", "Argument[3].Parameter[@0]", "value", "manual"]
- ["", "", False, "ReadFileEx", "", "", "Argument[*3].Field[@hEvent]", "Argument[4].Parameter[*2].Field[@hEvent]", "value", "manual"]

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +0,0 @@
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
- ["", "", False, "pthread_create", "", "", "Argument[@3]", "Argument[2].Parameter[@0]", "value", "manual"]

View File

@@ -1,11 +0,0 @@
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
- ["std", "thread", True, "thread", "", "", "Argument[*@1]", "Argument[0].Parameter[@0]", "value", "manual"]
- ["std", "thread", True, "thread", "", "", "Argument[*@2]", "Argument[0].Parameter[@1]", "value", "manual"]
- ["std", "thread", True, "thread", "", "", "Argument[*@3]", "Argument[0].Parameter[@2]", "value", "manual"]
- ["std", "thread", True, "thread", "", "", "Argument[*@4]", "Argument[0].Parameter[@3]", "value", "manual"]
- ["std", "thread", True, "thread", "", "", "Argument[*@5]", "Argument[0].Parameter[@4]", "value", "manual"]

View File

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

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