Merge branch 'main' into more-shell-taint

This commit is contained in:
erik-krogh
2023-02-27 18:27:09 +01:00
911 changed files with 40912 additions and 6898 deletions

View File

@@ -9,7 +9,7 @@ inputs:
outputs:
cache-dir:
description: "The directory where the cache was stored"
value: ${{ steps.fill-compilation-dir.outputs.compdir }}
value: ${{ steps.output-compilation-dir.outputs.compdir }}
runs:
using: composite
@@ -27,7 +27,9 @@ runs:
if: ${{ github.event_name == 'pull_request' }}
uses: actions/cache/restore@v3
with:
path: '**/.cache'
path: |
**/.cache
~/.codeql/compile-cache
key: codeql-compile-${{ inputs.key }}-pr-${{ github.sha }}
restore-keys: |
codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-${{ env.merge_base }}
@@ -37,18 +39,111 @@ runs:
if: ${{ github.event_name != 'pull_request' }}
uses: actions/cache@v3
with:
path: '**/.cache'
path: |
**/.cache
~/.codeql/compile-cache
key: codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-${{ github.sha }} # just fill on main
restore-keys: | # restore the latest cache if the exact cache is unavailable, to speed up compilation.
codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-
codeql-compile-${{ inputs.key }}-main-
- name: Fill compilation cache directory
id: fill-compilation-dir
- name: Output-compilationdir
id: output-compilation-dir
shell: bash
run: |
# Move all the existing cache into another folder, so we only preserve the cache for the current queries.
node $GITHUB_WORKSPACE/.github/actions/cache-query-compilation/move-caches.js ${COMBINED_CACHE_DIR}
echo "compdir=${COMBINED_CACHE_DIR}" >> $GITHUB_OUTPUT
env:
COMBINED_CACHE_DIR: ${{ runner.temp }}/compilation-dir
- name: Fill compilation cache directory
id: fill-compilation-dir
uses: actions/github-script@v6
env:
COMBINED_CACHE_DIR: ${{ runner.temp }}/compilation-dir
with:
script: |
// # Move all the existing cache into another folder, so we only preserve the cache for the current queries.
// mkdir -p ${COMBINED_CACHE_DIR}
// rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
// # copy the contents of the .cache folders into the combined cache folder.
// cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
// # clean up the .cache folders
// rm -rf **/.cache/*
const fs = require("fs");
const path = require("path");
const os = require("os");
// the first argv is the cache folder to create.
const COMBINED_CACHE_DIR = process.env.COMBINED_CACHE_DIR;
function* walkCaches(dir) {
const files = fs.readdirSync(dir, { withFileTypes: true });
for (const file of files) {
if (file.isDirectory()) {
const filePath = path.join(dir, file.name);
yield* walkCaches(filePath);
if (file.name === ".cache") {
yield filePath;
}
}
}
}
async function copyDir(src, dest) {
for await (const file of await fs.promises.readdir(src, { withFileTypes: true })) {
const srcPath = path.join(src, file.name);
const destPath = path.join(dest, file.name);
if (file.isDirectory()) {
if (!fs.existsSync(destPath)) {
fs.mkdirSync(destPath);
}
await copyDir(srcPath, destPath);
} else {
await fs.promises.copyFile(srcPath, destPath);
}
}
}
async function main() {
const cacheDirs = [...walkCaches(".")];
for (const dir of cacheDirs) {
console.log(`Found .cache dir at ${dir}`);
}
const globalCacheDir = path.join(os.homedir(), ".codeql", "compile-cache");
if (fs.existsSync(globalCacheDir)) {
console.log("Found global home dir: " + globalCacheDir);
cacheDirs.push(globalCacheDir);
}
if (cacheDirs.length === 0) {
console.log("No cache dirs found");
return;
}
// mkdir -p ${COMBINED_CACHE_DIR}
fs.mkdirSync(COMBINED_CACHE_DIR, { recursive: true });
// rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
await Promise.all(
cacheDirs.map((cacheDir) =>
(async function () {
await fs.promises.rm(path.join(cacheDir, "lock"), { force: true });
await fs.promises.rm(path.join(cacheDir, "size"), { force: true });
})()
)
);
// # copy the contents of the .cache folders into the combined cache folder.
// cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
await Promise.all(
cacheDirs.map((cacheDir) => copyDir(cacheDir, COMBINED_CACHE_DIR))
);
// # clean up the .cache folders
// rm -rf **/.cache/*
await Promise.all(
cacheDirs.map((cacheDir) => fs.promises.rm(cacheDir, { recursive: true }))
);
}
main();

View File

@@ -1,75 +0,0 @@
// # Move all the existing cache into another folder, so we only preserve the cache for the current queries.
// mkdir -p ${COMBINED_CACHE_DIR}
// rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
// # copy the contents of the .cache folders into the combined cache folder.
// cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
// # clean up the .cache folders
// rm -rf **/.cache/*
const fs = require("fs");
const path = require("path");
// the first argv is the cache folder to create.
const COMBINED_CACHE_DIR = process.argv[2];
function* walkCaches(dir) {
const files = fs.readdirSync(dir, { withFileTypes: true });
for (const file of files) {
if (file.isDirectory()) {
const filePath = path.join(dir, file.name);
yield* walkCaches(filePath);
if (file.name === ".cache") {
yield filePath;
}
}
}
}
async function copyDir(src, dest) {
for await (const file of await fs.promises.readdir(src, { withFileTypes: true })) {
const srcPath = path.join(src, file.name);
const destPath = path.join(dest, file.name);
if (file.isDirectory()) {
if (!fs.existsSync(destPath)) {
fs.mkdirSync(destPath);
}
await copyDir(srcPath, destPath);
} else {
await fs.promises.copyFile(srcPath, destPath);
}
}
}
async function main() {
const cacheDirs = [...walkCaches(".")];
for (const dir of cacheDirs) {
console.log(`Found .cache dir at ${dir}`);
}
// mkdir -p ${COMBINED_CACHE_DIR}
fs.mkdirSync(COMBINED_CACHE_DIR, { recursive: true });
// rm -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
await Promise.all(
cacheDirs.map((cacheDir) =>
(async function () {
await fs.promises.rm(path.join(cacheDir, "lock"), { force: true });
await fs.promises.rm(path.join(cacheDir, "size"), { force: true });
})()
)
);
// # copy the contents of the .cache folders into the combined cache folder.
// cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
await Promise.all(
cacheDirs.map((cacheDir) => copyDir(cacheDir, COMBINED_CACHE_DIR))
);
// # clean up the .cache folders
// rm -rf **/.cache/*
await Promise.all(
cacheDirs.map((cacheDir) => fs.promises.rm(cacheDir, { recursive: true }))
);
}
main();

View File

@@ -24,14 +24,14 @@ jobs:
with:
key: all-queries
- name: check formatting
run: find */ql -type f \( -name "*.qll" -o -name "*.ql" \) -print0 | xargs -0 codeql query format --check-only
run: find */ql -type f \( -name "*.qll" -o -name "*.ql" \) -print0 | xargs -0 -n 3000 -P 10 codeql query format -q --check-only
- name: compile queries - check-only
# run with --check-only if running in a PR (github.sha != main)
if : ${{ github.event_name == 'pull_request' }}
shell: bash
run: codeql query compile -j0 */ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
- name: compile queries - full
# do full compile if running on main - this populates the cache
if : ${{ github.event_name != 'pull_request' }}
shell: bash
run: codeql query compile -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
run: codeql query compile -q -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"

View File

@@ -5,13 +5,6 @@ on:
branches: [main]
pull_request:
branches: [main]
paths:
- "ql/**"
- "**.qll"
- "**.ql"
- "**.dbscheme"
- "**/qlpack.yml"
- ".github/workflows/ql-for-ql-build.yml"
env:
CARGO_TERM_COLOR: always
@@ -22,6 +15,8 @@ jobs:
steps:
### Build the queries ###
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@v2
@@ -34,7 +29,9 @@ jobs:
id: cache-extractor
uses: actions/cache@v3
with:
path: ql/extractor-pack/
path: |
ql/extractor-pack/
ql/target/release/buramu
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
- name: Cache cargo
if: steps.cache-extractor.outputs.cache-hit != 'true'
@@ -57,6 +54,7 @@ jobs:
key: run-ql-for-ql
- name: Make database and analyze
run: |
./ql/target/release/buramu | tee deprecated.blame # Add a blame file for the extractor to parse.
${CODEQL} database create -l=ql --search-path ql/extractor-pack ${DB}
${CODEQL} database analyze -j0 --format=sarif-latest --output=ql-for-ql.sarif ${DB} ql/ql/src/codeql-suites/ql-code-scanning.qls --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
@@ -65,6 +63,7 @@ jobs:
LGTM_INDEX_FILTERS: |
exclude:ql/ql/test
exclude:*/ql/lib/upgrades/
exclude:java/ql/integration-tests
- name: Upload sarif to code-scanning
uses: github/codeql-action/upload-sarif@v2
with:

View File

@@ -5,6 +5,7 @@ on:
paths:
- "swift/**"
- "misc/bazel/**"
- "misc/codegen/**"
- "*.bazel*"
- .github/workflows/swift.yml
- .github/actions/**
@@ -19,6 +20,7 @@ on:
paths:
- "swift/**"
- "misc/bazel/**"
- "misc/codegen/**"
- "*.bazel*"
- .github/workflows/swift.yml
- .github/actions/**

View File

@@ -53,5 +53,5 @@ repos:
name: Run Swift code generation unit tests
files: ^swift/codegen/.*\.py$
language: system
entry: bazel test //swift/codegen/test
entry: bazel test //misc/codegen/test
pass_filenames: false

View File

@@ -2,10 +2,11 @@
/csharp/ @github/codeql-csharp
/go/ @github/codeql-go
/java/ @github/codeql-java
/javascript/ @github/codeql-javascript
/python/ @github/codeql-python
/ruby/ @github/codeql-ruby
/javascript/ @github/codeql-dynamic
/python/ @github/codeql-dynamic
/ruby/ @github/codeql-dynamic
/swift/ @github/codeql-swift
/misc/codegen/ @github/codeql-swift
/java/kotlin-extractor/ @github/codeql-kotlin
/java/kotlin-explorer/ @github/codeql-kotlin

View File

@@ -131,6 +131,14 @@ namespace Semmle.Autobuild.Cpp.Tests
bool IBuildActions.IsWindows() => IsWindows;
public bool IsMacOs { get; set; }
bool IBuildActions.IsMacOs() => IsMacOs;
public bool IsArm { get; set; }
bool IBuildActions.IsArm() => IsArm;
string IBuildActions.PathCombine(params string[] parts)
{
return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p)));

View File

@@ -1,3 +1,7 @@
## 0.5.3
No user-facing changes.
## 0.5.2
No user-facing changes.

View File

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.5.2
lastReleaseVersion: 0.5.3

View File

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

View File

@@ -1,3 +1,7 @@
## 0.5.3
No user-facing changes.
## 0.5.2
No user-facing changes.

View File

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.5.2
lastReleaseVersion: 0.5.3

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.5.3-dev
version: 0.5.4-dev
groups:
- cpp
- queries

View File

@@ -145,6 +145,14 @@ namespace Semmle.Autobuild.CSharp.Tests
bool IBuildActions.IsWindows() => IsWindows;
public bool IsMacOs { get; set; }
bool IBuildActions.IsMacOs() => IsMacOs;
public bool IsArm { get; set; }
bool IBuildActions.IsArm() => IsArm;
public string PathCombine(params string[] parts)
{
return string.Join(IsWindows ? '\\' : '/', parts.Where(p => !string.IsNullOrWhiteSpace(p)));

View File

@@ -7,6 +7,7 @@ using System.Xml;
using System.Net.Http;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace Semmle.Autobuild.Shared
{
@@ -98,6 +99,18 @@ namespace Semmle.Autobuild.Shared
/// </summary>
bool IsWindows();
/// <summary>
/// Gets a value indicating whether we are running on macOS.
/// </summary>
/// <returns>True if we are running on macOS.</returns>
bool IsMacOs();
/// <summary>
/// Gets a value indicating whether we are running on arm.
/// </summary>
/// <returns>True if we are running on arm.</returns>
bool IsArm();
/// <summary>
/// Combine path segments, Path.Combine().
/// </summary>
@@ -203,6 +216,12 @@ namespace Semmle.Autobuild.Shared
bool IBuildActions.IsWindows() => Win32.IsWindows();
bool IBuildActions.IsMacOs() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
bool IBuildActions.IsArm() =>
RuntimeInformation.ProcessArchitecture == Architecture.Arm64 ||
RuntimeInformation.ProcessArchitecture == Architecture.Arm;
string IBuildActions.PathCombine(params string[] parts) => Path.Combine(parts);
void IBuildActions.WriteAllText(string filename, string contents) => File.WriteAllText(filename, contents);

View File

@@ -1,18 +1,36 @@
using Semmle.Util.Logging;
using System;
using System.Linq;
using System.Runtime.InteropServices;
namespace Semmle.Autobuild.Shared
{
internal static class MsBuildCommandExtensions
{
/// <summary>
/// Appends a call to msbuild.
/// </summary>
/// <param name="cmdBuilder"></param>
/// <param name="builder"></param>
/// <returns></returns>
public static CommandBuilder MsBuildCommand(this CommandBuilder cmdBuilder, IAutobuilder<AutobuildOptionsShared> builder)
{
var isArmMac = builder.Actions.IsMacOs() && builder.Actions.IsArm();
// mono doesn't ship with `msbuild` on Arm-based Macs, but we can fall back to
// msbuild that ships with `dotnet` which can be invoked with `dotnet msbuild`
// perhaps we should do this on all platforms?
return isArmMac ?
cmdBuilder.RunCommand("dotnet").Argument("msbuild") :
cmdBuilder.RunCommand("msbuild");
}
}
/// <summary>
/// A build rule using msbuild.
/// </summary>
public class MsBuildRule : IBuildRule<AutobuildOptionsShared>
{
/// <summary>
/// The name of the msbuild command.
/// </summary>
private const string msBuild = "msbuild";
public BuildScript Analyse(IAutobuilder<AutobuildOptionsShared> builder, bool auto)
{
if (!builder.ProjectsOrSolutionsToBuild.Any())
@@ -57,7 +75,7 @@ namespace Semmle.Autobuild.Shared
Script;
var nugetRestore = GetNugetRestoreScript();
var msbuildRestoreCommand = new CommandBuilder(builder.Actions).
RunCommand(msBuild).
MsBuildCommand(builder).
Argument("/t:restore").
QuoteArgument(projectOrSolution.FullPath);
@@ -95,7 +113,7 @@ namespace Semmle.Autobuild.Shared
command.RunCommand("set Platform=&& type NUL", quoteExe: false);
}
command.RunCommand(msBuild);
command.MsBuildCommand(builder);
command.QuoteArgument(projectOrSolution.FullPath);
var target = builder.Options.MsBuildTarget ?? "rebuild";

View File

@@ -65,6 +65,15 @@ namespace Semmle.Extraction.CSharp.Entities
trapFile.has_modifiers(target, Modifier.Create(cx, modifier));
}
private static void ExtractFieldModifiers(Context cx, TextWriter trapFile, IEntity key, IFieldSymbol symbol)
{
if (symbol.IsReadOnly)
HasModifier(cx, trapFile, key, Modifiers.Readonly);
if (symbol.IsRequired)
HasModifier(cx, trapFile, key, Modifiers.Required);
}
private static void ExtractNamedTypeModifiers(Context cx, TextWriter trapFile, IEntity key, ISymbol symbol)
{
if (symbol.Kind != SymbolKind.NamedType)
@@ -106,8 +115,11 @@ namespace Semmle.Extraction.CSharp.Entities
if (symbol.IsVirtual)
HasModifier(cx, trapFile, key, Modifiers.Virtual);
if (symbol.Kind == SymbolKind.Field && ((IFieldSymbol)symbol).IsReadOnly)
HasModifier(cx, trapFile, key, Modifiers.Readonly);
if (symbol is IFieldSymbol field)
ExtractFieldModifiers(cx, trapFile, key, field);
if (symbol.Kind == SymbolKind.Property && ((IPropertySymbol)symbol).IsRequired)
HasModifier(cx, trapFile, key, Modifiers.Required);
if (symbol.IsOverride)
HasModifier(cx, trapFile, key, Modifiers.Override);

View File

@@ -13,6 +13,7 @@ internal static class Modifiers
public const string Public = "public";
public const string Readonly = "readonly";
public const string Record = "record";
public const string Required = "required";
public const string Ref = "ref";
public const string Sealed = "sealed";
public const string Static = "static";

View File

@@ -77,12 +77,8 @@ namespace Semmle.Extraction.CSharp
/// <summary>
/// Gets the source-level modifiers belonging to this symbol, if any.
/// </summary>
public static IEnumerable<string> GetSourceLevelModifiers(this ISymbol symbol)
{
var methodModifiers = symbol.GetModifiers<Microsoft.CodeAnalysis.CSharp.Syntax.BaseMethodDeclarationSyntax>(md => md.Modifiers);
var typeModifiers = symbol.GetModifiers<Microsoft.CodeAnalysis.CSharp.Syntax.TypeDeclarationSyntax>(cd => cd.Modifiers);
return methodModifiers.Concat(typeModifiers).Select(m => m.Text);
}
public static IEnumerable<string> GetSourceLevelModifiers(this ISymbol symbol) =>
symbol.GetModifiers<Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax>(md => md.Modifiers).Select(m => m.Text);
/// <summary>
/// Holds if the ID generated for `dependant` will contain a reference to

View File

@@ -1,3 +1,7 @@
## 1.4.3
No user-facing changes.
## 1.4.2
No user-facing changes.

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,7 @@
## 1.4.3
No user-facing changes.
## 1.4.2
No user-facing changes.

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,13 @@
using System;
namespace Test
{
public class Program
{
public static int Main(string[] args)
{
Console.WriteLine("Hello world!");
return 0;
}
}
}

View File

@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net4.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,4 @@
from create_database_utils import *
# force CodeQL to use MSBuild by setting `LGTM_INDEX_MSBUILD_TARGET`
run_codeql_database_create([], test_db="default-db", db=None, lang="csharp", extra_env={ 'LGTM_INDEX_MSBUILD_TARGET': 'Build' })

View File

@@ -1,3 +1,9 @@
## 0.5.3
### Minor Analysis Improvements
* C# 11: Added extractor support for the `scoped` modifier annotation on parameters and local variables.
## 0.5.2
### Major Analysis Improvements

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* C# 11: Added extractor support for the `scoped` modifier annotation on parameters and local variables.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* C# 11: Added library support for `checked` operators.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* C# 11: Added extractor support for `required` fields and properties.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The extraction of member modifiers has been generalised, which could lead to the extraction of more modifiers.

View File

@@ -0,0 +1,5 @@
## 0.5.3
### Minor Analysis Improvements
* C# 11: Added extractor support for the `scoped` modifier annotation on parameters and local variables.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.5.2
lastReleaseVersion: 0.5.3

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-all
version: 0.5.3-dev
version: 0.5.4-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp
@@ -8,6 +8,7 @@ upgrades: upgrades
dependencies:
codeql/ssa: ${workspace}
codeql/tutorial: ${workspace}
codeql/util: ${workspace}
dataExtensions:
- ext/*.model.yml
- ext/generated/*.model.yml

View File

@@ -435,8 +435,12 @@ class Destructor extends DotNet::Destructor, Callable, Member, Attributable, @de
* (`BinaryOperator`), or a conversion operator (`ConversionOperator`).
*/
class Operator extends Callable, Member, Attributable, @operator {
/** Gets the assembly name of this operator. */
string getAssemblyName() { operators(this, result, _, _, _, _) }
/**
* DEPRECATED: use `getFunctionName()` instead.
*
* Gets the assembly name of this operator.
*/
deprecated string getAssemblyName() { result = this.getFunctionName() }
override string getName() { operators(this, _, result, _, _, _) }
@@ -445,7 +449,7 @@ class Operator extends Callable, Member, Attributable, @operator {
/**
* Gets the metadata name of the operator, such as `op_implicit` or `op_RightShift`.
*/
string getFunctionName() { none() }
string getFunctionName() { operators(this, result, _, _, _, _) }
override ValueOrRefType getDeclaringType() { operators(this, _, _, result, _, _) }
@@ -481,10 +485,11 @@ class RecordCloneMethod extends Method, DotNet::RecordCloneCallable {
* A user-defined unary operator - an operator taking one operand.
*
* Either a plus operator (`PlusOperator`), minus operator (`MinusOperator`),
* not operator (`NotOperator`), complement operator (`ComplementOperator`),
* true operator (`TrueOperator`), false operator (`FalseOperator`),
* increment operator (`IncrementOperator`), or decrement operator
* (`DecrementOperator`).
* checked minus operator (`CheckedMinusOperator`), not operator (`NotOperator`),
* complement operator (`ComplementOperator`), true operator (`TrueOperator`),
* false operator (`FalseOperator`), increment operator (`IncrementOperator`),
* checked increment operator (`CheckedIncrementOperator`), decrement operator
* (`DecrementOperator`) or checked decrement operator (`CheckedDecrementOperator`).
*/
class UnaryOperator extends Operator {
UnaryOperator() {
@@ -505,8 +510,6 @@ class UnaryOperator extends Operator {
class PlusOperator extends UnaryOperator {
PlusOperator() { this.getName() = "+" }
override string getFunctionName() { result = "op_UnaryPlus" }
override string getAPrimaryQlClass() { result = "PlusOperator" }
}
@@ -522,11 +525,24 @@ class PlusOperator extends UnaryOperator {
class MinusOperator extends UnaryOperator {
MinusOperator() { this.getName() = "-" }
override string getFunctionName() { result = "op_UnaryNegation" }
override string getAPrimaryQlClass() { result = "MinusOperator" }
}
/**
* A user-defined checked minus operator (`-`), for example
*
* ```csharp
* public static Widget operator checked -(Widget w) {
* ...
* }
* ```
*/
class CheckedMinusOperator extends UnaryOperator {
CheckedMinusOperator() { this.getName() = "checked -" }
override string getAPrimaryQlClass() { result = "CheckedMinusOperator" }
}
/**
* A user-defined not operator (`!`), for example
*
@@ -539,8 +555,6 @@ class MinusOperator extends UnaryOperator {
class NotOperator extends UnaryOperator {
NotOperator() { this.getName() = "!" }
override string getFunctionName() { result = "op_LogicalNot" }
override string getAPrimaryQlClass() { result = "NotOperator" }
}
@@ -556,8 +570,6 @@ class NotOperator extends UnaryOperator {
class ComplementOperator extends UnaryOperator {
ComplementOperator() { this.getName() = "~" }
override string getFunctionName() { result = "op_OnesComplement" }
override string getAPrimaryQlClass() { result = "ComplementOperator" }
}
@@ -573,11 +585,24 @@ class ComplementOperator extends UnaryOperator {
class IncrementOperator extends UnaryOperator {
IncrementOperator() { this.getName() = "++" }
override string getFunctionName() { result = "op_Increment" }
override string getAPrimaryQlClass() { result = "IncrementOperator" }
}
/**
* A user-defined checked increment operator (`++`), for example
*
* ```csharp
* public static Widget operator checked ++(Widget w) {
* ...
* }
* ```
*/
class CheckedIncrementOperator extends UnaryOperator {
CheckedIncrementOperator() { this.getName() = "checked ++" }
override string getAPrimaryQlClass() { result = "CheckedIncrementOperator" }
}
/**
* A user-defined decrement operator (`--`), for example
*
@@ -590,11 +615,24 @@ class IncrementOperator extends UnaryOperator {
class DecrementOperator extends UnaryOperator {
DecrementOperator() { this.getName() = "--" }
override string getFunctionName() { result = "op_Decrement" }
override string getAPrimaryQlClass() { result = "DecrementOperator" }
}
/**
* A user-defined checked decrement operator (`--`), for example
*
* ```csharp
* public static Widget operator checked --(Widget w) {
* ...
* }
* ```
*/
class CheckedDecrementOperator extends UnaryOperator {
CheckedDecrementOperator() { this.getName() = "checked --" }
override string getAPrimaryQlClass() { result = "CheckedDecrementOperator" }
}
/**
* A user-defined false operator (`false`), for example
*
@@ -607,8 +645,6 @@ class DecrementOperator extends UnaryOperator {
class FalseOperator extends UnaryOperator {
FalseOperator() { this.getName() = "false" }
override string getFunctionName() { result = "op_False" }
override string getAPrimaryQlClass() { result = "FalseOperator" }
}
@@ -624,17 +660,18 @@ class FalseOperator extends UnaryOperator {
class TrueOperator extends UnaryOperator {
TrueOperator() { this.getName() = "true" }
override string getFunctionName() { result = "op_True" }
override string getAPrimaryQlClass() { result = "TrueOperator" }
}
/**
* A user-defined binary operator.
*
* Either an addition operator (`AddOperator`), a subtraction operator
* (`SubOperator`), a multiplication operator (`MulOperator`), a division
* operator (`DivOperator`), a remainder operator (`RemOperator`), an and
* Either an addition operator (`AddOperator`), a checked addition operator
* (`CheckedAddOperator`) a subtraction operator (`SubOperator`), a checked
* substraction operator (`CheckedSubOperator`), a multiplication operator
* (`MulOperator`), a checked multiplication operator (`CheckedMulOperator`),
* a division operator (`DivOperator`), a checked division operator
* (`CheckedDivOperator`), a remainder operator (`RemOperator`), an and
* operator (`AndOperator`), an or operator (`OrOperator`), an xor
* operator (`XorOperator`), a left shift operator (`LeftShiftOperator`),
* a right shift operator (`RightShiftOperator`), an unsigned right shift
@@ -659,11 +696,24 @@ class BinaryOperator extends Operator {
class AddOperator extends BinaryOperator {
AddOperator() { this.getName() = "+" }
override string getFunctionName() { result = "op_Addition" }
override string getAPrimaryQlClass() { result = "AddOperator" }
}
/**
* A user-defined checked addition operator (`+`), for example
*
* ```csharp
* public static Widget operator checked +(Widget lhs, Widget rhs) {
* ...
* }
* ```
*/
class CheckedAddOperator extends BinaryOperator {
CheckedAddOperator() { this.getName() = "checked +" }
override string getAPrimaryQlClass() { result = "CheckedAddOperator" }
}
/**
* A user-defined subtraction operator (`-`), for example
*
@@ -676,11 +726,24 @@ class AddOperator extends BinaryOperator {
class SubOperator extends BinaryOperator {
SubOperator() { this.getName() = "-" }
override string getFunctionName() { result = "op_Subtraction" }
override string getAPrimaryQlClass() { result = "SubOperator" }
}
/**
* A user-defined checked subtraction operator (`-`), for example
*
* ```csharp
* public static Widget operator checked -(Widget lhs, Widget rhs) {
* ...
* }
* ```
*/
class CheckedSubOperator extends BinaryOperator {
CheckedSubOperator() { this.getName() = "checked -" }
override string getAPrimaryQlClass() { result = "CheckedSubOperator" }
}
/**
* A user-defined multiplication operator (`*`), for example
*
@@ -693,11 +756,24 @@ class SubOperator extends BinaryOperator {
class MulOperator extends BinaryOperator {
MulOperator() { this.getName() = "*" }
override string getFunctionName() { result = "op_Multiply" }
override string getAPrimaryQlClass() { result = "MulOperator" }
}
/**
* A user-defined checked multiplication operator (`*`), for example
*
* ```csharp
* public static Widget operator checked *(Widget lhs, Widget rhs) {
* ...
* }
* ```
*/
class CheckedMulOperator extends BinaryOperator {
CheckedMulOperator() { this.getName() = "checked *" }
override string getAPrimaryQlClass() { result = "CheckedMulOperator" }
}
/**
* A user-defined division operator (`/`), for example
*
@@ -710,11 +786,24 @@ class MulOperator extends BinaryOperator {
class DivOperator extends BinaryOperator {
DivOperator() { this.getName() = "/" }
override string getFunctionName() { result = "op_Division" }
override string getAPrimaryQlClass() { result = "DivOperator" }
}
/**
* A user-defined checked division operator (`/`), for example
*
* ```csharp
* public static Widget operator checked /(Widget lhs, Widget rhs) {
* ...
* }
* ```
*/
class CheckedDivOperator extends BinaryOperator {
CheckedDivOperator() { this.getName() = "checked /" }
override string getAPrimaryQlClass() { result = "CheckedDivOperator" }
}
/**
* A user-defined remainder operator (`%`), for example
*
@@ -727,8 +816,6 @@ class DivOperator extends BinaryOperator {
class RemOperator extends BinaryOperator {
RemOperator() { this.getName() = "%" }
override string getFunctionName() { result = "op_Modulus" }
override string getAPrimaryQlClass() { result = "RemOperator" }
}
@@ -744,8 +831,6 @@ class RemOperator extends BinaryOperator {
class AndOperator extends BinaryOperator {
AndOperator() { this.getName() = "&" }
override string getFunctionName() { result = "op_BitwiseAnd" }
override string getAPrimaryQlClass() { result = "AndOperator" }
}
@@ -761,8 +846,6 @@ class AndOperator extends BinaryOperator {
class OrOperator extends BinaryOperator {
OrOperator() { this.getName() = "|" }
override string getFunctionName() { result = "op_BitwiseOr" }
override string getAPrimaryQlClass() { result = "OrOperator" }
}
@@ -778,8 +861,6 @@ class OrOperator extends BinaryOperator {
class XorOperator extends BinaryOperator {
XorOperator() { this.getName() = "^" }
override string getFunctionName() { result = "op_ExclusiveOr" }
override string getAPrimaryQlClass() { result = "XorOperator" }
}
@@ -795,8 +876,6 @@ class XorOperator extends BinaryOperator {
class LeftShiftOperator extends BinaryOperator {
LeftShiftOperator() { this.getName() = "<<" }
override string getFunctionName() { result = "op_LeftShift" }
override string getAPrimaryQlClass() { result = "LeftShiftOperator" }
}
@@ -815,8 +894,6 @@ deprecated class LShiftOperator = LeftShiftOperator;
class RightShiftOperator extends BinaryOperator {
RightShiftOperator() { this.getName() = ">>" }
override string getFunctionName() { result = "op_RightShift" }
override string getAPrimaryQlClass() { result = "RightShiftOperator" }
}
@@ -835,8 +912,6 @@ deprecated class RShiftOperator = RightShiftOperator;
class UnsignedRightShiftOperator extends BinaryOperator {
UnsignedRightShiftOperator() { this.getName() = ">>>" }
override string getFunctionName() { result = "op_UnsignedRightShift" }
override string getAPrimaryQlClass() { result = "UnsignedRightShiftOperator" }
}
@@ -852,8 +927,6 @@ class UnsignedRightShiftOperator extends BinaryOperator {
class EQOperator extends BinaryOperator {
EQOperator() { this.getName() = "==" }
override string getFunctionName() { result = "op_Equality" }
override string getAPrimaryQlClass() { result = "EQOperator" }
}
@@ -869,8 +942,6 @@ class EQOperator extends BinaryOperator {
class NEOperator extends BinaryOperator {
NEOperator() { this.getName() = "!=" }
override string getFunctionName() { result = "op_Inequality" }
override string getAPrimaryQlClass() { result = "NEOperator" }
}
@@ -886,8 +957,6 @@ class NEOperator extends BinaryOperator {
class LTOperator extends BinaryOperator {
LTOperator() { this.getName() = "<" }
override string getFunctionName() { result = "op_LessThan" }
override string getAPrimaryQlClass() { result = "LTOperator" }
}
@@ -903,8 +972,6 @@ class LTOperator extends BinaryOperator {
class GTOperator extends BinaryOperator {
GTOperator() { this.getName() = ">" }
override string getFunctionName() { result = "op_GreaterThan" }
override string getAPrimaryQlClass() { result = "GTOperator" }
}
@@ -920,8 +987,6 @@ class GTOperator extends BinaryOperator {
class LEOperator extends BinaryOperator {
LEOperator() { this.getName() = "<=" }
override string getFunctionName() { result = "op_LessThanOrEqual" }
override string getAPrimaryQlClass() { result = "LEOperator" }
}
@@ -937,8 +1002,6 @@ class LEOperator extends BinaryOperator {
class GEOperator extends BinaryOperator {
GEOperator() { this.getName() = ">=" }
override string getFunctionName() { result = "op_GreaterThanOrEqual" }
override string getAPrimaryQlClass() { result = "GEOperator" }
}
@@ -954,7 +1017,8 @@ class GEOperator extends BinaryOperator {
class ConversionOperator extends Operator {
ConversionOperator() {
this.getName() = "implicit conversion" or
this.getName() = "explicit conversion"
this.getName() = "explicit conversion" or
this.getName() = "checked explicit conversion"
}
/** Gets the source type of the conversion. */
@@ -976,8 +1040,6 @@ class ConversionOperator extends Operator {
class ImplicitConversionOperator extends ConversionOperator {
ImplicitConversionOperator() { this.getName() = "implicit conversion" }
override string getFunctionName() { result = "op_Implicit" }
override string getAPrimaryQlClass() { result = "ImplicitConversionOperator" }
}
@@ -993,11 +1055,24 @@ class ImplicitConversionOperator extends ConversionOperator {
class ExplicitConversionOperator extends ConversionOperator {
ExplicitConversionOperator() { this.getName() = "explicit conversion" }
override string getFunctionName() { result = "op_Explicit" }
override string getAPrimaryQlClass() { result = "ExplicitConversionOperator" }
}
/**
* A user-defined checked explicit conversion operator, for example
*
* ```csharp
* public static explicit operator checked int(BigInteger i) {
* ...
* }
* ```
*/
class CheckedExplicitConversionOperator extends ConversionOperator {
CheckedExplicitConversionOperator() { this.getName() = "checked explicit conversion" }
override string getAPrimaryQlClass() { result = "CheckedExplicitConversionOperator" }
}
/**
* A local function, defined within the scope of another callable.
* For example, `Fac` on lines 2--4 in

View File

@@ -3,184 +3,34 @@
*/
private import Comments
private import codeql.util.FileSystem
/** A file or folder. */
class Container extends @container {
/**
* Gets the absolute, canonical path of this container, using forward slashes
* as path separator.
*
* The path starts with a _root prefix_ followed by zero or more _path
* segments_ separated by forward slashes.
*
* The root prefix is of one of the following forms:
*
* 1. A single forward slash `/` (Unix-style)
* 2. An upper-case drive letter followed by a colon and a forward slash,
* such as `C:/` (Windows-style)
* 3. Two forward slashes, a computer name, and then another forward slash,
* such as `//FileServer/` (UNC-style)
*
* Path segments are never empty (that is, absolute paths never contain two
* contiguous slashes, except as part of a UNC-style root prefix). Also, path
* segments never contain forward slashes, and no path segment is of the
* form `.` (one dot) or `..` (two dots).
*
* Note that an absolute path never ends with a forward slash, except if it is
* a bare root prefix, that is, the path has no path segments. A container
* whose absolute path has no segments is always a `Folder`, not a `File`.
*/
string getAbsolutePath() { none() }
private module Input implements InputSig {
abstract class ContainerBase extends @container {
abstract string getAbsolutePath();
/**
* Gets a URL representing the location of this container.
*
* For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls).
*/
string getURL() { none() }
ContainerBase getParentContainer() { containerparent(result, this) }
/**
* Gets the relative path of this file or folder from the root folder of the
* analyzed source location. The relative path of the root folder itself is
* the empty string.
*
* This has no result if the container is outside the source root, that is,
* if the root folder is not a reflexive, transitive parent of this container.
*/
string getRelativePath() {
exists(string absPath, string pref |
absPath = this.getAbsolutePath() and sourceLocationPrefix(pref)
|
absPath = pref and result = ""
or
absPath = pref.regexpReplaceAll("/$", "") + "/" + result and
not result.matches("/%")
)
string toString() { result = this.getAbsolutePath() }
}
/**
* Gets the base name of this container including extension, that is, the last
* segment of its absolute path, or the empty string if it has no segments.
*
* Here are some examples of absolute paths and the corresponding base names
* (surrounded with quotes to avoid ambiguity):
*
* <table border="1">
* <tr><th>Absolute path</th><th>Base name</th></tr>
* <tr><td>"/tmp/tst.cs"</td><td>"tst.cs"</td></tr>
* <tr><td>"C:/Program Files (x86)"</td><td>"Program Files (x86)"</td></tr>
* <tr><td>"/"</td><td>""</td></tr>
* <tr><td>"C:/"</td><td>""</td></tr>
* <tr><td>"D:/"</td><td>""</td></tr>
* <tr><td>"//FileServer/"</td><td>""</td></tr>
* </table>
*/
string getBaseName() {
result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
class FolderBase extends ContainerBase, @folder {
override string getAbsolutePath() { folders(this, result) }
}
/**
* Gets the extension of this container, that is, the suffix of its base name
* after the last dot character, if any.
*
* In particular,
*
* - if the name does not include a dot, there is no extension, so this
* predicate has no result;
* - if the name ends in a dot, the extension is the empty string;
* - if the name contains multiple dots, the extension follows the last dot.
*
* Here are some examples of absolute paths and the corresponding extensions
* (surrounded with quotes to avoid ambiguity):
*
* <table border="1">
* <tr><th>Absolute path</th><th>Extension</th></tr>
* <tr><td>"/tmp/tst.cs"</td><td>"cs"</td></tr>
* <tr><td>"/tmp/.classpath"</td><td>"classpath"</td></tr>
* <tr><td>"/bin/bash"</td><td>not defined</td></tr>
* <tr><td>"/tmp/tst2."</td><td>""</td></tr>
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
* </table>
*/
string getExtension() {
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3)
class FileBase extends ContainerBase, @file {
override string getAbsolutePath() { files(this, result) }
}
/**
* Gets the stem of this container, that is, the prefix of its base name up to
* (but not including) the last dot character if there is one, or the entire
* base name if there is not.
*
* Here are some examples of absolute paths and the corresponding stems
* (surrounded with quotes to avoid ambiguity):
*
* <table border="1">
* <tr><th>Absolute path</th><th>Stem</th></tr>
* <tr><td>"/tmp/tst.cs"</td><td>"tst"</td></tr>
* <tr><td>"/tmp/.classpath"</td><td>""</td></tr>
* <tr><td>"/bin/bash"</td><td>"bash"</td></tr>
* <tr><td>"/tmp/tst2."</td><td>"tst2"</td></tr>
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
* </table>
*/
string getStem() {
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1)
}
/** Gets the parent container of this file or folder, if any. */
Container getParentContainer() { containerparent(result, this) }
/** Gets a file or sub-folder in this container. */
Container getAChildContainer() { this = result.getParentContainer() }
/** Gets a file in this container. */
File getAFile() { result = this.getAChildContainer() }
/** Gets the file in this container that has the given `baseName`, if any. */
File getFile(string baseName) {
result = this.getAFile() and
result.getBaseName() = baseName
}
/** Gets a sub-folder in this container. */
Folder getAFolder() { result = this.getAChildContainer() }
/** Gets the sub-folder in this container that has the given `baseName`, if any. */
Folder getFolder(string baseName) {
result = this.getAFolder() and
result.getBaseName() = baseName
}
/** Gets the file or sub-folder in this container that has the given `name`, if any. */
Container getChildContainer(string name) {
result = this.getAChildContainer() and
result.getBaseName() = name
}
/** Gets the file in this container that has the given `stem` and `extension`, if any. */
File getFile(string stem, string extension) {
result = this.getAChildContainer() and
result.getStem() = stem and
result.getExtension() = extension
}
/** Gets a sub-folder contained in this container. */
Folder getASubFolder() { result = this.getAChildContainer() }
/**
* Gets a textual representation of the path of this container.
*
* This is the absolute path of the container.
*/
string toString() { result = this.getAbsolutePath() }
predicate hasSourceLocationPrefix = sourceLocationPrefix/1;
}
private module Impl = Make<Input>;
class Container = Impl::Container;
/** A folder. */
class Folder extends Container, @folder {
override string getAbsolutePath() { folders(this, result) }
override string getURL() { result = "folder://" + this.getAbsolutePath() }
}
class Folder extends Container, Impl::Folder { }
bindingset[flag]
private predicate fileHasExtractionFlag(File f, int flag) {
@@ -191,9 +41,7 @@ private predicate fileHasExtractionFlag(File f, int flag) {
}
/** A file. */
class File extends Container, @file {
override string getAbsolutePath() { files(this, result) }
class File extends Container, Impl::File {
/** Gets the number of lines in this file. */
int getNumberOfLines() { numlines(this, result, _, _) }

View File

@@ -90,6 +90,9 @@ class Modifiable extends Declaration, @modifiable {
/** Holds if this declaration is `const`. */
predicate isConst() { this.hasModifier("const") }
/** Holds if this declaration has the modifier `required`. */
predicate isRequired() { this.hasModifier("required") }
/** Holds if this declaration is `unsafe`. */
predicate isUnsafe() {
this.hasModifier("unsafe") or
@@ -178,6 +181,8 @@ class Member extends DotNet::Member, Modifiable, @member {
override predicate isAbstract() { Modifiable.super.isAbstract() }
override predicate isStatic() { Modifiable.super.isStatic() }
override predicate isRequired() { Modifiable.super.isRequired() }
}
private class TOverridable = @virtualizable or @callable_accessor;

View File

@@ -80,6 +80,9 @@ class Member extends Declaration, @dotnet_member {
/** Holds if this member is `static`. */
predicate isStatic() { none() }
/** Holds if this member is declared `required`. */
predicate isRequired() { none() }
/**
* Holds if this member has name `name` and is defined in type `type`
* with namespace `namespace`.

View File

@@ -1,3 +1,7 @@
## 0.5.3
No user-facing changes.
## 0.5.2
No user-facing changes.

View File

@@ -8,6 +8,7 @@ private import semmle.code.csharp.dataflow.FlowSummary
private import semmle.code.csharp.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
private import semmle.code.csharp.security.dataflow.flowsources.Remote
@@ -104,8 +105,17 @@ class ExternalApi extends DotNet::Callable {
pragma[nomagic]
predicate isSink() { sinkNode(this.getAnInput(), _) }
/** Holds if this API is supported by existing CodeQL libraries, that is, it is either a recognized source or sink or has a flow summary. */
predicate isSupported() { this.hasSummary() or this.isSource() or this.isSink() }
/** Holds if this API is a known neutral. */
pragma[nomagic]
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
/**
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
* recognized source, sink or neutral or it has a flow summary.
*/
predicate isSupported() {
this.hasSummary() or this.isSource() or this.isSink() or this.isNeutral()
}
}
/**

View File

@@ -8,13 +8,9 @@
private import csharp
private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import ExternalApi
private predicate relevant(ExternalApi api) {
api.isSupported() or
api instanceof FlowSummaryImpl::Public::NeutralCallable
}
private predicate relevant(ExternalApi api) { api.isSupported() }
from string info, int usages
where Results<relevant/1>::restrict(info, usages)

View File

@@ -7,14 +7,9 @@
*/
private import csharp
private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import ExternalApi
private predicate relevant(ExternalApi api) {
not api.isSupported() and
not api instanceof FlowSummaryImpl::Public::NeutralCallable
}
private predicate relevant(ExternalApi api) { not api.isSupported() }
from string info, int usages
where Results<relevant/1>::restrict(info, usages)

View File

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

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.5.2
lastReleaseVersion: 0.5.3

View File

@@ -9,13 +9,10 @@
*/
private import csharp
private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import Telemetry.ExternalApi
from Call c, ExternalApi api
where
c.getTarget().getUnboundDeclaration() = api and
not api.isSupported() and
not api instanceof FlowSummaryImpl::Public::NeutralCallable
not api.isSupported()
select c, "Call to unsupported external API $@.", api, api.toString()

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries
version: 0.5.3-dev
version: 0.5.4-dev
groups:
- csharp
- queries

View File

@@ -0,0 +1,56 @@
namespace CheckedOperators;
public class Number
{
public int Value { get; }
public Number(int n) => this.Value = n;
public static Number operator checked +(Number n1, Number n2) =>
new Number(checked(n1.Value + n2.Value));
public static Number operator +(Number n1, Number n2) =>
new Number(n1.Value + n2.Value);
public static Number operator checked -(Number n1, Number n2) =>
new Number(checked(n1.Value - n2.Value));
public static Number operator -(Number n1, Number n2) =>
new Number(n1.Value - n2.Value);
public static Number operator checked *(Number n1, Number n2) =>
new Number(checked(n1.Value * n2.Value));
public static Number operator *(Number n1, Number n2) =>
new Number(n1.Value * n2.Value);
public static Number operator checked /(Number n1, Number n2) =>
new Number(checked(n1.Value / n2.Value));
public static Number operator /(Number n1, Number n2) =>
new Number(n1.Value / n2.Value);
public static Number operator checked -(Number n) =>
new Number(checked(-n.Value));
public static Number operator -(Number n) =>
new Number(-n.Value);
public static Number operator checked ++(Number n) =>
new Number(checked(n.Value + 1));
public static Number operator ++(Number n) =>
new Number(n.Value + 1);
public static Number operator checked --(Number n) =>
new Number(checked(n.Value - 1));
public static Number operator --(Number n) =>
new Number(n.Value - 1);
public static explicit operator short(Number n) =>
(short)n.Value;
public static explicit operator checked short(Number n) =>
checked((short)n.Value);
}

View File

@@ -1,3 +1,221 @@
CheckedOperators.cs:
# 1| [NamespaceDeclaration] namespace ... { ... }
# 3| 1: [Class] Number
# 5| 4: [Property] Value
# 5| -1: [TypeMention] int
# 5| 3: [Getter] get_Value
# 7| 5: [InstanceConstructor] Number
#-----| 2: (Parameters)
# 7| 0: [Parameter] n
# 7| -1: [TypeMention] int
# 7| 4: [AssignExpr] ... = ...
# 7| 0: [PropertyCall] access to property Value
# 7| -1: [ThisAccess] this access
# 7| 1: [ParameterAccess] access to parameter n
# 9| 6: [CheckedAddOperator] checked +
# 9| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 9| 0: [Parameter] n1
# 9| -1: [TypeMention] Number
# 9| 1: [Parameter] n2
# 9| -1: [TypeMention] Number
# 10| 4: [ObjectCreation] object creation of type Number
# 10| -1: [TypeMention] Number
# 10| 0: [CheckedExpr] checked (...)
# 10| 0: [AddExpr] ... + ...
# 10| 0: [PropertyCall] access to property Value
# 10| -1: [ParameterAccess] access to parameter n1
# 10| 1: [PropertyCall] access to property Value
# 10| -1: [ParameterAccess] access to parameter n2
# 12| 7: [AddOperator] +
# 12| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 12| 0: [Parameter] n1
# 12| -1: [TypeMention] Number
# 12| 1: [Parameter] n2
# 12| -1: [TypeMention] Number
# 13| 4: [ObjectCreation] object creation of type Number
# 13| -1: [TypeMention] Number
# 13| 0: [AddExpr] ... + ...
# 13| 0: [PropertyCall] access to property Value
# 13| -1: [ParameterAccess] access to parameter n1
# 13| 1: [PropertyCall] access to property Value
# 13| -1: [ParameterAccess] access to parameter n2
# 15| 8: [CheckedSubOperator] checked -
# 15| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 15| 0: [Parameter] n1
# 15| -1: [TypeMention] Number
# 15| 1: [Parameter] n2
# 15| -1: [TypeMention] Number
# 16| 4: [ObjectCreation] object creation of type Number
# 16| -1: [TypeMention] Number
# 16| 0: [CheckedExpr] checked (...)
# 16| 0: [SubExpr] ... - ...
# 16| 0: [PropertyCall] access to property Value
# 16| -1: [ParameterAccess] access to parameter n1
# 16| 1: [PropertyCall] access to property Value
# 16| -1: [ParameterAccess] access to parameter n2
# 18| 9: [SubOperator] -
# 18| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 18| 0: [Parameter] n1
# 18| -1: [TypeMention] Number
# 18| 1: [Parameter] n2
# 18| -1: [TypeMention] Number
# 19| 4: [ObjectCreation] object creation of type Number
# 19| -1: [TypeMention] Number
# 19| 0: [SubExpr] ... - ...
# 19| 0: [PropertyCall] access to property Value
# 19| -1: [ParameterAccess] access to parameter n1
# 19| 1: [PropertyCall] access to property Value
# 19| -1: [ParameterAccess] access to parameter n2
# 21| 10: [CheckedMulOperator] checked *
# 21| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 21| 0: [Parameter] n1
# 21| -1: [TypeMention] Number
# 21| 1: [Parameter] n2
# 21| -1: [TypeMention] Number
# 22| 4: [ObjectCreation] object creation of type Number
# 22| -1: [TypeMention] Number
# 22| 0: [CheckedExpr] checked (...)
# 22| 0: [MulExpr] ... * ...
# 22| 0: [PropertyCall] access to property Value
# 22| -1: [ParameterAccess] access to parameter n1
# 22| 1: [PropertyCall] access to property Value
# 22| -1: [ParameterAccess] access to parameter n2
# 24| 11: [MulOperator] *
# 24| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 24| 0: [Parameter] n1
# 24| -1: [TypeMention] Number
# 24| 1: [Parameter] n2
# 24| -1: [TypeMention] Number
# 25| 4: [ObjectCreation] object creation of type Number
# 25| -1: [TypeMention] Number
# 25| 0: [MulExpr] ... * ...
# 25| 0: [PropertyCall] access to property Value
# 25| -1: [ParameterAccess] access to parameter n1
# 25| 1: [PropertyCall] access to property Value
# 25| -1: [ParameterAccess] access to parameter n2
# 27| 12: [CheckedDivOperator] checked /
# 27| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 27| 0: [Parameter] n1
# 27| -1: [TypeMention] Number
# 27| 1: [Parameter] n2
# 27| -1: [TypeMention] Number
# 28| 4: [ObjectCreation] object creation of type Number
# 28| -1: [TypeMention] Number
# 28| 0: [CheckedExpr] checked (...)
# 28| 0: [DivExpr] ... / ...
# 28| 0: [PropertyCall] access to property Value
# 28| -1: [ParameterAccess] access to parameter n1
# 28| 1: [PropertyCall] access to property Value
# 28| -1: [ParameterAccess] access to parameter n2
# 30| 13: [DivOperator] /
# 30| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 30| 0: [Parameter] n1
# 30| -1: [TypeMention] Number
# 30| 1: [Parameter] n2
# 30| -1: [TypeMention] Number
# 31| 4: [ObjectCreation] object creation of type Number
# 31| -1: [TypeMention] Number
# 31| 0: [DivExpr] ... / ...
# 31| 0: [PropertyCall] access to property Value
# 31| -1: [ParameterAccess] access to parameter n1
# 31| 1: [PropertyCall] access to property Value
# 31| -1: [ParameterAccess] access to parameter n2
# 33| 14: [CheckedMinusOperator] checked -
# 33| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 33| 0: [Parameter] n
# 33| -1: [TypeMention] Number
# 34| 4: [ObjectCreation] object creation of type Number
# 34| -1: [TypeMention] Number
# 34| 0: [CheckedExpr] checked (...)
# 34| 0: [UnaryMinusExpr] -...
# 34| 0: [PropertyCall] access to property Value
# 34| -1: [ParameterAccess] access to parameter n
# 36| 15: [MinusOperator] -
# 36| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 36| 0: [Parameter] n
# 36| -1: [TypeMention] Number
# 37| 4: [ObjectCreation] object creation of type Number
# 37| -1: [TypeMention] Number
# 37| 0: [UnaryMinusExpr] -...
# 37| 0: [PropertyCall] access to property Value
# 37| -1: [ParameterAccess] access to parameter n
# 39| 16: [CheckedIncrementOperator] checked ++
# 39| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 39| 0: [Parameter] n
# 39| -1: [TypeMention] Number
# 40| 4: [ObjectCreation] object creation of type Number
# 40| -1: [TypeMention] Number
# 40| 0: [CheckedExpr] checked (...)
# 40| 0: [AddExpr] ... + ...
# 40| 0: [PropertyCall] access to property Value
# 40| -1: [ParameterAccess] access to parameter n
# 40| 1: [IntLiteral] 1
# 42| 17: [IncrementOperator] ++
# 42| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 42| 0: [Parameter] n
# 42| -1: [TypeMention] Number
# 43| 4: [ObjectCreation] object creation of type Number
# 43| -1: [TypeMention] Number
# 43| 0: [AddExpr] ... + ...
# 43| 0: [PropertyCall] access to property Value
# 43| -1: [ParameterAccess] access to parameter n
# 43| 1: [IntLiteral] 1
# 45| 18: [CheckedDecrementOperator] checked --
# 45| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 45| 0: [Parameter] n
# 45| -1: [TypeMention] Number
# 46| 4: [ObjectCreation] object creation of type Number
# 46| -1: [TypeMention] Number
# 46| 0: [CheckedExpr] checked (...)
# 46| 0: [SubExpr] ... - ...
# 46| 0: [PropertyCall] access to property Value
# 46| -1: [ParameterAccess] access to parameter n
# 46| 1: [IntLiteral] 1
# 48| 19: [DecrementOperator] --
# 48| -1: [TypeMention] Number
#-----| 2: (Parameters)
# 48| 0: [Parameter] n
# 48| -1: [TypeMention] Number
# 49| 4: [ObjectCreation] object creation of type Number
# 49| -1: [TypeMention] Number
# 49| 0: [SubExpr] ... - ...
# 49| 0: [PropertyCall] access to property Value
# 49| -1: [ParameterAccess] access to parameter n
# 49| 1: [IntLiteral] 1
# 51| 20: [ExplicitConversionOperator] explicit conversion
# 51| -1: [TypeMention] short
#-----| 2: (Parameters)
# 51| 0: [Parameter] n
# 51| -1: [TypeMention] Number
# 52| 4: [CastExpr] (...) ...
# 52| 0: [TypeAccess] access to type Int16
# 52| 0: [TypeMention] short
# 52| 1: [PropertyCall] access to property Value
# 52| -1: [ParameterAccess] access to parameter n
# 54| 21: [CheckedExplicitConversionOperator] checked explicit conversion
# 54| -1: [TypeMention] short
#-----| 2: (Parameters)
# 54| 0: [Parameter] n
# 54| -1: [TypeMention] Number
# 55| 4: [CheckedExpr] checked (...)
# 55| 0: [CastExpr] (...) ...
# 55| 0: [TypeAccess] access to type Int16
# 55| 0: [TypeMention] short
# 55| 1: [PropertyCall] access to property Value
# 55| -1: [ParameterAccess] access to parameter n
GenericAttribute.cs:
# 3| [GenericAssemblyAttribute] [assembly: MyGeneric<Int32>(...)]
# 3| 0: [TypeMention] MyGenericAttribute<int>
@@ -439,6 +657,97 @@ RelaxedShift.cs:
# 30| 1: [OperatorCall] call to operator >>>
# 30| 0: [LocalVariableAccess] access to local variable n31
# 30| 1: [StringLiteralUtf16] "3"
RequiredMembers.cs:
# 4| [Class] ClassRequiredMembers
# 6| 4: [Field] RequiredField
# 6| -1: [TypeMention] object
# 7| 5: [Property] RequiredProperty
# 7| -1: [TypeMention] string
# 7| 3: [Getter] get_RequiredProperty
# 7| 4: [Setter] set_RequiredProperty
#-----| 2: (Parameters)
# 7| 0: [Parameter] value
# 8| 6: [Property] VirtualProperty
# 8| -1: [TypeMention] object
# 8| 3: [Getter] get_VirtualProperty
# 8| 4: [Setter] set_VirtualProperty
#-----| 2: (Parameters)
# 8| 0: [Parameter] value
# 10| 7: [InstanceConstructor] ClassRequiredMembers
# 10| 4: [BlockStmt] {...}
# 13| 8: [InstanceConstructor] ClassRequiredMembers
#-----| 0: (Attributes)
# 12| 1: [DefaultAttribute] [SetsRequiredMembers(...)]
# 12| 0: [TypeMention] SetsRequiredMembersAttribute
#-----| 2: (Parameters)
# 13| 0: [Parameter] requiredField
# 13| -1: [TypeMention] object
# 13| 1: [Parameter] requiredProperty
# 13| -1: [TypeMention] string
# 14| 4: [BlockStmt] {...}
# 15| 0: [ExprStmt] ...;
# 15| 0: [AssignExpr] ... = ...
# 15| 0: [FieldAccess] access to field RequiredField
# 15| 1: [ParameterAccess] access to parameter requiredField
# 16| 1: [ExprStmt] ...;
# 16| 0: [AssignExpr] ... = ...
# 16| 0: [PropertyCall] access to property RequiredProperty
# 16| 1: [ParameterAccess] access to parameter requiredProperty
# 20| [Class] ClassRequiredMembersSub
#-----| 3: (Base types)
# 20| 0: [TypeMention] ClassRequiredMembers
# 22| 4: [Property] VirtualProperty
# 22| -1: [TypeMention] object
# 22| 3: [Getter] get_VirtualProperty
# 22| 4: [Setter] set_VirtualProperty
#-----| 2: (Parameters)
# 22| 0: [Parameter] value
# 24| 5: [InstanceConstructor] ClassRequiredMembersSub
# 24| 3: [ConstructorInitializer] call to constructor ClassRequiredMembers
# 24| 4: [BlockStmt] {...}
# 27| 6: [InstanceConstructor] ClassRequiredMembersSub
#-----| 0: (Attributes)
# 26| 1: [DefaultAttribute] [SetsRequiredMembers(...)]
# 26| 0: [TypeMention] SetsRequiredMembersAttribute
#-----| 2: (Parameters)
# 27| 0: [Parameter] requiredField
# 27| -1: [TypeMention] object
# 27| 1: [Parameter] requiredProperty
# 27| -1: [TypeMention] string
# 27| 2: [Parameter] virtualProperty
# 27| -1: [TypeMention] object
# 27| 3: [ConstructorInitializer] call to constructor ClassRequiredMembers
# 27| 0: [ParameterAccess] access to parameter requiredField
# 27| 1: [ParameterAccess] access to parameter requiredProperty
# 28| 4: [BlockStmt] {...}
# 29| 0: [ExprStmt] ...;
# 29| 0: [AssignExpr] ... = ...
# 29| 0: [PropertyCall] access to property VirtualProperty
# 29| 1: [ParameterAccess] access to parameter virtualProperty
# 33| [RecordClass] RecordRequiredMembers
# 33| 12: [NEOperator] !=
#-----| 2: (Parameters)
# 33| 0: [Parameter] left
# 33| 1: [Parameter] right
# 33| 13: [EQOperator] ==
#-----| 2: (Parameters)
# 33| 0: [Parameter] left
# 33| 1: [Parameter] right
# 33| 14: [Property] EqualityContract
# 33| 3: [Getter] get_EqualityContract
# 35| 15: [Property] X
# 35| -1: [TypeMention] object
# 35| 3: [Getter] get_X
# 35| 4: [Setter] set_X
#-----| 2: (Parameters)
# 35| 0: [Parameter] value
# 38| [Struct] StructRequiredMembers
# 40| 5: [Property] Y
# 40| -1: [TypeMention] string
# 40| 3: [Getter] get_Y
# 40| 4: [Setter] set_Y
#-----| 2: (Parameters)
# 40| 0: [Parameter] value
Scoped.cs:
# 1| [Struct] S1
# 2| [Struct] S2

View File

@@ -0,0 +1,42 @@
using System;
using System.Diagnostics.CodeAnalysis;
public class ClassRequiredMembers
{
public required object? RequiredField;
public required string? RequiredProperty { get; init; }
public virtual object? VirtualProperty { get; init; }
public ClassRequiredMembers() { }
[SetsRequiredMembers]
public ClassRequiredMembers(object requiredField, string requiredProperty)
{
RequiredField = requiredField;
RequiredProperty = requiredProperty;
}
}
public class ClassRequiredMembersSub : ClassRequiredMembers
{
public override required object? VirtualProperty { get; init; }
public ClassRequiredMembersSub() : base() { }
[SetsRequiredMembers]
public ClassRequiredMembersSub(object requiredField, string requiredProperty, object virtualProperty) : base(requiredField, requiredProperty)
{
VirtualProperty = virtualProperty;
}
}
public record RecordRequiredMembers
{
public required object? X { get; init; }
}
public struct StructRequiredMembers
{
public required string? Y { get; init; }
}

View File

@@ -0,0 +1,16 @@
| CheckedOperators.cs:9:43:9:43 | checked + | op_CheckedAddition | CheckedAddOperator |
| CheckedOperators.cs:12:35:12:35 | + | op_Addition | AddOperator |
| CheckedOperators.cs:15:43:15:43 | checked - | op_CheckedSubtraction | CheckedSubOperator |
| CheckedOperators.cs:18:35:18:35 | - | op_Subtraction | SubOperator |
| CheckedOperators.cs:21:43:21:43 | checked * | op_CheckedMultiply | CheckedMulOperator |
| CheckedOperators.cs:24:35:24:35 | * | op_Multiply | MulOperator |
| CheckedOperators.cs:27:43:27:43 | checked / | op_CheckedDivision | CheckedDivOperator |
| CheckedOperators.cs:30:35:30:35 | / | op_Division | DivOperator |
| CheckedOperators.cs:33:43:33:43 | checked - | op_CheckedUnaryNegation | CheckedMinusOperator |
| CheckedOperators.cs:36:35:36:35 | - | op_UnaryNegation | MinusOperator |
| CheckedOperators.cs:39:43:39:44 | checked ++ | op_CheckedIncrement | CheckedIncrementOperator |
| CheckedOperators.cs:42:35:42:36 | ++ | op_Increment | IncrementOperator |
| CheckedOperators.cs:45:43:45:44 | checked -- | op_CheckedDecrement | CheckedDecrementOperator |
| CheckedOperators.cs:48:35:48:36 | -- | op_Decrement | DecrementOperator |
| CheckedOperators.cs:51:28:51:35 | explicit conversion | op_Explicit | ExplicitConversionOperator |
| CheckedOperators.cs:54:28:54:35 | checked explicit conversion | op_CheckedExplicit | CheckedExplicitConversionOperator |

View File

@@ -0,0 +1,5 @@
import csharp
from Operator o
where o.getFile().getStem() = "CheckedOperators"
select o, o.getFunctionName(), o.getAPrimaryQlClass()

View File

@@ -0,0 +1,5 @@
| RequiredMembers.cs:6:29:6:41 | RequiredField | ClassRequiredMembers | Field |
| RequiredMembers.cs:7:29:7:44 | RequiredProperty | ClassRequiredMembers | Property |
| RequiredMembers.cs:22:38:22:52 | VirtualProperty | ClassRequiredMembersSub | Property |
| RequiredMembers.cs:35:29:35:29 | X | RecordRequiredMembers | Property |
| RequiredMembers.cs:40:29:40:29 | Y | StructRequiredMembers | Property |

View File

@@ -0,0 +1,8 @@
import csharp
query predicate requiredmembers(Member m, string type, string qlclass) {
m.getFile().getStem() = "RequiredMembers" and
m.isRequired() and
type = m.getDeclaringType().getName() and
qlclass = m.getAPrimaryQlClass()
}

View File

@@ -238,3 +238,7 @@
| ViableCallable.cs:458:10:458:14 | M5<> | ViableCallable.cs:444:23:444:27 | M2<> |
| ViableCallable.cs:475:10:475:12 | Run | ViableCallable.cs:468:10:468:11 | M2 |
| ViableCallable.cs:475:10:475:12 | Run | ViableCallable.cs:473:17:473:18 | M1 |
| ViableCallable.cs:492:10:492:12 | Run | ViableCallable.cs:487:32:487:32 | + |
| ViableCallable.cs:492:10:492:12 | Run | ViableCallable.cs:488:40:488:40 | checked + |
| ViableCallable.cs:492:10:492:12 | Run | ViableCallable.cs:489:28:489:35 | explicit conversion |
| ViableCallable.cs:492:10:492:12 | Run | ViableCallable.cs:490:28:490:35 | checked explicit conversion |

View File

@@ -471,3 +471,7 @@
| ViableCallable.cs:461:9:461:30 | call to method M2<T> | C17.M2<T>(Func<T>) |
| ViableCallable.cs:478:9:478:14 | call to method M1 | C18.M1() |
| ViableCallable.cs:481:9:481:14 | call to method M2 | I2.M2() |
| ViableCallable.cs:495:18:495:22 | call to operator + | C19.+(C19, C19) |
| ViableCallable.cs:498:26:498:30 | call to operator checked + | C19.checked +(C19, C19) |
| ViableCallable.cs:501:18:501:23 | call to operator explicit conversion | C19.explicit conversion(C19) |
| ViableCallable.cs:504:26:504:31 | call to operator checked explicit conversion | C19.checked explicit conversion(C19) |

View File

@@ -480,4 +480,27 @@ class C18 : I2
// Viable callables: I2.M2()
i.M2();
}
}
class C19
{
public static C19 operator +(C19 x, C19 y) => throw null;
public static C19 operator checked +(C19 x, C19 y) => throw null;
public static explicit operator int(C19 x) => throw null;
public static explicit operator checked int(C19 x) => throw null;
void Run(C19 c)
{
// Viable callables: C19.op_Addition()
var c1 = c + c;
// Viable callables: C19.op_CheckedAddition()
var c2 = checked(c + c);
// Viable callables: C19.op_Explicit()
var n1 = (int)c;
// Viable callables: C19.op_CheckedExplicit()
var n2 = checked((int)c);
}
}

View File

@@ -268,3 +268,7 @@
| ViableCallable.cs:423:9:423:21 | call to method M<String> | M<> | A5 |
| ViableCallable.cs:478:9:478:14 | call to method M1 | M1 | C18 |
| ViableCallable.cs:481:9:481:14 | call to method M2 | M2 | I2 |
| ViableCallable.cs:495:18:495:22 | call to operator + | + | C19 |
| ViableCallable.cs:498:26:498:30 | call to operator checked + | checked + | C19 |
| ViableCallable.cs:501:18:501:23 | call to operator explicit conversion | explicit conversion | C19 |
| ViableCallable.cs:504:26:504:31 | call to operator checked explicit conversion | checked explicit conversion | C19 |

View File

@@ -6,7 +6,11 @@ if [[ "$OSTYPE" == "linux-gnu"* ]]; then
dotnet_platform="linux-x64"
elif [[ "$OSTYPE" == "darwin"* ]]; then
platform="osx64"
dotnet_platform="osx-x64"
if [[ $(uname -m) == 'arm64' ]]; then
dotnet_platform="osx-arm64"
else
dotnet_platform="osx-x64"
fi
else
echo "Unknown OS"
exit 1

View File

@@ -204,6 +204,7 @@ The completed query will now identify cases where the result of ``strlen`` is st
.. code-block:: ql
import cpp
import semmle.code.cpp.controlflow.SSA
class MallocCall extends FunctionCall
{

View File

@@ -16,8 +16,8 @@
.NET Core up to 3.1
.NET 5, .NET 6","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
Go (aka Golang), "Go up to 1.19", "Go 1.11 or more recent", ``.go``
Java,"Java 7 to 19 [4]_","javac (OpenJDK and Oracle JDK),
Go (aka Golang), "Go up to 1.20", "Go 1.11 or more recent", ``.go``
Java,"Java 7 to 20 [4]_","javac (OpenJDK and Oracle JDK),
Eclipse compiler for Java (ECJ) [5]_",``.java``
Kotlin [6]_,"Kotlin 1.5.0 to 1.8.20","kotlinc",``.kt``
@@ -31,7 +31,7 @@
.. [1] C++20 support is currently in beta. Supported for GCC on Linux only. Modules are *not* supported.
.. [2] Support for the clang-cl compiler is preliminary.
.. [3] Support for the Arm Compiler (armcc) is preliminary.
.. [4] Builds that execute on Java 7 to 19 can be analyzed. The analysis understands Java 19 standard language features.
.. [4] Builds that execute on Java 7 to 20 can be analyzed. The analysis understands Java 20 standard language features.
.. [5] ECJ is supported when the build invokes it via the Maven Compiler plugin or the Takari Lifecycle plugin.
.. [6] Kotlin support is currently in beta.
.. [7] JSX and Flow code, YAML, JSON, HTML, and XML files may also be analyzed with JavaScript files.

View File

@@ -1,3 +1,13 @@
## 0.4.3
### New Features
* Go 1.20 is now supported. The extractor now functions as expected when Go 1.20 is installed; the definition of `implementsComparable` has been updated according to Go 1.20's new, more-liberal rules; and taint flow models have been added for relevant, new standard-library functions.
### Minor Analysis Improvements
* Support for the Twirp framework has been added.
## 0.4.2
No user-facing changes.

View File

@@ -1,2 +0,0 @@
lgtm,codescanning
* Support for the Twirp framework has been added.

View File

@@ -0,0 +1,9 @@
## 0.4.3
### New Features
* Go 1.20 is now supported. The extractor now functions as expected when Go 1.20 is installed; the definition of `implementsComparable` has been updated according to Go 1.20's new, more-liberal rules; and taint flow models have been added for relevant, new standard-library functions.
### Minor Analysis Improvements
* Support for the Twirp framework has been added.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.4.2
lastReleaseVersion: 0.4.3

View File

@@ -1,5 +1,5 @@
name: codeql/go-all
version: 0.4.3-dev
version: 0.4.4-dev
groups: go
dbscheme: go.dbscheme
extractor: go

View File

@@ -112,22 +112,10 @@ class Type extends @type {
or
u instanceof ArrayType and u.(ArrayType).getElementType().implementsComparable()
or
exists(InterfaceType uif | uif = u |
not uif instanceof BasicInterfaceType and
if exists(uif.getAnEmbeddedTypeSetLiteral())
then
// All types in the intersection of all the embedded type set
// literals must implement comparable.
forall(Type intersectionType |
intersectionType = uif.getAnEmbeddedTypeSetLiteral().getATerm().getType() and
forall(TypeSetLiteralType tslit | tslit = uif.getAnEmbeddedTypeSetLiteral() |
intersectionType = tslit.getATerm().getType()
)
|
intersectionType.implementsComparable()
)
else uif.isOrEmbedsComparable()
)
// As of Go 1.20, any interface type satisfies the `comparable` constraint, even though comparison
// may panic at runtime depending on the actual object's concrete type.
// Look at git history here if you need the old definition.
u instanceof InterfaceType
)
}

View File

@@ -65,6 +65,7 @@ import semmle.go.frameworks.stdlib.Syscall
import semmle.go.frameworks.stdlib.TextScanner
import semmle.go.frameworks.stdlib.TextTabwriter
import semmle.go.frameworks.stdlib.TextTemplate
import semmle.go.frameworks.stdlib.Unsafe
/** A `String()` method. */
class StringMethod extends TaintTracking::FunctionModel, Method {

View File

@@ -11,6 +11,15 @@ module Bytes {
FunctionOutput outp;
FunctionModels() {
hasQualifiedName("bytes", "Clone") and
(inp.isParameter(0) and outp.isResult())
or
hasQualifiedName("bytes", "Cut") and
(inp.isParameter(0) and outp.isResult([0, 1]))
or
hasQualifiedName("bytes", ["CutPrefix", "CutSuffix"]) and
(inp.isParameter(0) and outp.isResult(0))
or
// signature: func Fields(s []byte) [][]byte
hasQualifiedName("bytes", "Fields") and
(inp.isParameter(0) and outp.isResult())

View File

@@ -22,6 +22,10 @@ module Errors {
// signature: func Unwrap(err error) error
hasQualifiedName("errors", "Unwrap") and
(inp.isParameter(0) and outp.isResult())
or
// signature: func Join(errs ...error) error
hasQualifiedName("errors", "Join") and
(inp.isParameter(_) and outp.isResult())
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {

View File

@@ -11,6 +11,9 @@ module Sync {
FunctionOutput outp;
MethodModels() {
hasQualifiedName("sync", "Map", "CompareAndSwap") and
(inp.isParameter(2) and outp.isReceiver())
or
// signature: func (*Map) Load(key interface{}) (value interface{}, ok bool)
hasQualifiedName("sync", "Map", "Load") and
(inp.isReceiver() and outp.isResult(0))
@@ -28,6 +31,13 @@ module Sync {
hasQualifiedName("sync", "Map", "Store") and
(inp.isParameter(_) and outp.isReceiver())
or
hasQualifiedName("sync", "Map", "Swap") and
(
inp.isReceiver() and outp.isResult(0)
or
inp.isParameter(_) and outp.isReceiver()
)
or
// signature: func (*Pool) Get() interface{}
hasQualifiedName("sync", "Pool", "Get") and
(inp.isReceiver() and outp.isResult())

View File

@@ -0,0 +1,22 @@
/**
* Provides classes modeling security-relevant aspects of the `unsafe` package.
*/
import go
/** Provides models of commonly used functions in the `unsafe` package. */
module Unsafe {
private class FunctionModels extends TaintTracking::FunctionModel {
FunctionInput inp;
FunctionOutput outp;
FunctionModels() {
hasQualifiedName("unsafe", ["String", "StringData", "Slice", "SliceData"]) and
(inp.isParameter(0) and outp.isResult())
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input = inp and output = outp
}
}
}

View File

@@ -56,14 +56,14 @@ private predicate isIncorrectIntegerConversion(int sourceBitSize, int sinkBitSiz
* integer types, which could cause unexpected values.
*/
class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
boolean sourceIsSigned;
boolean sinkIsSigned;
int sourceBitSize;
int sinkBitSize;
ConversionWithoutBoundsCheckConfig() {
sourceIsSigned in [true, false] and
sinkIsSigned in [true, false] and
isIncorrectIntegerConversion(sourceBitSize, sinkBitSize) and
this = "ConversionWithoutBoundsCheckConfig" + sourceBitSize + sourceIsSigned + sinkBitSize
this = "ConversionWithoutBoundsCheckConfig" + sourceBitSize + sinkIsSigned + sinkBitSize
}
/** Gets the bit size of the source. */
@@ -75,11 +75,6 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
|
c.getTarget() = ip and source = c.getResult(0)
|
(
if ip.getResultType(0) instanceof SignedIntegerType
then sourceIsSigned = true
else sourceIsSigned = false
) and
(
apparentBitSize = ip.getTargetBitSize()
or
@@ -112,10 +107,13 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
predicate isSinkWithBitSize(DataFlow::TypeCastNode sink, int bitSize) {
sink.asExpr() instanceof ConversionExpr and
exists(IntegerType integerType | sink.getResultType().getUnderlyingType() = integerType |
bitSize = integerType.getSize()
or
not exists(integerType.getSize()) and
bitSize = getIntTypeBitSize(sink.getFile())
(
bitSize = integerType.getSize()
or
not exists(integerType.getSize()) and
bitSize = getIntTypeBitSize(sink.getFile())
) and
if integerType instanceof SignedIntegerType then sinkIsSigned = true else sinkIsSigned = false
) and
not exists(ShrExpr shrExpr |
shrExpr.getLeftOperand().getGlobalValueNumber() =
@@ -134,7 +132,7 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
if sinkBitSize != 0 then bitSize = sinkBitSize else bitSize = 32
|
node = DataFlow::BarrierGuard<upperBoundCheckGuard/3>::getABarrierNodeForGuard(g) and
g.isBoundFor(bitSize, sourceIsSigned)
g.isBoundFor(bitSize, sinkIsSigned)
)
}

View File

@@ -1,3 +1,13 @@
## 0.4.3
### New Queries
* Added a new query, `go/unhandled-writable-file-close`, to detect instances where writable file handles are closed without appropriate checks for errors.
### Query Metadata Changes
* The precision of the `go/log-injection` query was decreased from `high` to `medium`, since it may not be able to identify every way in which log data may be sanitized. This also aligns it with the precision of comparable queries for other languages.
## 0.4.2
No user-facing changes.

View File

@@ -1,4 +0,0 @@
---
category: newQuery
---
* Added a new query, `go/unhandled-writable-file-close`, to detect instances where writable file handles are closed without appropriate checks for errors.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The query `go/incorrect-integer-conversion` now correctly recognises guards of the form `if val <= x` to protect a conversion `uintX(val)` when `x` is in the range `(math.MaxIntX, math.MaxUintX]`.

View File

@@ -1,4 +1,9 @@
---
category: queryMetadata
---
## 0.4.3
### New Queries
* Added a new query, `go/unhandled-writable-file-close`, to detect instances where writable file handles are closed without appropriate checks for errors.
### Query Metadata Changes
* The precision of the `go/log-injection` query was decreased from `high` to `medium`, since it may not be able to identify every way in which log data may be sanitized. This also aligns it with the precision of comparable queries for other languages.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.4.2
lastReleaseVersion: 0.4.3

View File

@@ -1,5 +1,5 @@
name: codeql/go-queries
version: 0.4.3-dev
version: 0.4.4-dev
groups:
- go
- queries

View File

@@ -51,31 +51,31 @@
| interface.go:95:6:95:8 | i18 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i18 |
| interface.go:101:6:101:8 | i19 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i19 |
| interface.go:105:6:105:8 | i20 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.i20 |
| interface.go:110:6:110:19 | testComparable | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable |
| interface.go:111:6:111:20 | testComparable0 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable0 |
| interface.go:112:6:112:20 | testComparable1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable1 |
| interface.go:113:6:113:20 | testComparable2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable2 |
| interface.go:114:6:114:20 | testComparable3 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable3 |
| interface.go:115:6:115:20 | testComparable4 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable4 |
| interface.go:116:6:116:20 | testComparable5 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable5 |
| interface.go:117:6:117:20 | testComparable6 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable6 |
| interface.go:118:6:118:20 | testComparable7 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable7 |
| interface.go:119:6:119:20 | testComparable8 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable8 |
| interface.go:120:6:120:20 | testComparable9 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable9 |
| interface.go:121:6:121:21 | testComparable10 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable10 |
| interface.go:122:6:122:21 | testComparable11 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable11 |
| interface.go:123:6:123:21 | testComparable12 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable12 |
| interface.go:124:6:124:21 | testComparable13 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable13 |
| interface.go:125:6:125:21 | testComparable14 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable14 |
| interface.go:126:6:126:21 | testComparable15 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable15 |
| interface.go:127:6:127:21 | testComparable16 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable16 |
| interface.go:128:6:128:21 | testComparable17 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable17 |
| interface.go:129:6:129:21 | testComparable18 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable18 |
| interface.go:130:6:130:21 | testComparable19 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable19 |
| interface.go:131:6:131:21 | testComparable20 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable20 |
| interface.go:132:6:132:21 | testComparable21 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable21 |
| interface.go:133:6:133:21 | testComparable22 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable22 |
| interface.go:134:6:134:21 | testComparable23 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable23 |
| interface.go:114:6:114:19 | testComparable | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable |
| interface.go:115:6:115:20 | testComparable0 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable0 |
| interface.go:116:6:116:20 | testComparable1 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable1 |
| interface.go:117:6:117:20 | testComparable2 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable2 |
| interface.go:118:6:118:20 | testComparable3 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable3 |
| interface.go:119:6:119:20 | testComparable4 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable4 |
| interface.go:120:6:120:20 | testComparable5 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable5 |
| interface.go:121:6:121:20 | testComparable6 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable6 |
| interface.go:122:6:122:20 | testComparable7 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable7 |
| interface.go:123:6:123:20 | testComparable8 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable8 |
| interface.go:124:6:124:20 | testComparable9 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable9 |
| interface.go:125:6:125:21 | testComparable10 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable10 |
| interface.go:126:6:126:21 | testComparable11 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable11 |
| interface.go:127:6:127:21 | testComparable12 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable12 |
| interface.go:128:6:128:21 | testComparable13 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable13 |
| interface.go:129:6:129:21 | testComparable14 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable14 |
| interface.go:130:6:130:21 | testComparable15 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable15 |
| interface.go:131:6:131:21 | testComparable16 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable16 |
| interface.go:132:6:132:21 | testComparable17 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable17 |
| interface.go:133:6:133:21 | testComparable18 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable18 |
| interface.go:134:6:134:21 | testComparable19 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable19 |
| interface.go:135:6:135:21 | testComparable20 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable20 |
| interface.go:136:6:136:21 | testComparable21 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable21 |
| interface.go:137:6:137:21 | testComparable22 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable22 |
| interface.go:138:6:138:21 | testComparable23 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types.testComparable23 |
| pkg1/embedding.go:8:6:8:9 | base | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.base |
| pkg1/embedding.go:19:6:19:13 | embedder | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.embedder |
| pkg1/embedding.go:22:6:22:16 | ptrembedder | github.com/github/codeql-go/ql/test/library-tests/semmle/go/Types/pkg1.ptrembedder |

View File

@@ -51,31 +51,31 @@
| interface.go:95:6:95:8 | i18 | i18 |
| interface.go:101:6:101:8 | i19 | i19 |
| interface.go:105:6:105:8 | i20 | i20 |
| interface.go:110:6:110:19 | testComparable | testComparable |
| interface.go:111:6:111:20 | testComparable0 | testComparable0 |
| interface.go:112:6:112:20 | testComparable1 | testComparable1 |
| interface.go:113:6:113:20 | testComparable2 | testComparable2 |
| interface.go:114:6:114:20 | testComparable3 | testComparable3 |
| interface.go:115:6:115:20 | testComparable4 | testComparable4 |
| interface.go:116:6:116:20 | testComparable5 | testComparable5 |
| interface.go:117:6:117:20 | testComparable6 | testComparable6 |
| interface.go:118:6:118:20 | testComparable7 | testComparable7 |
| interface.go:119:6:119:20 | testComparable8 | testComparable8 |
| interface.go:120:6:120:20 | testComparable9 | testComparable9 |
| interface.go:121:6:121:21 | testComparable10 | testComparable10 |
| interface.go:122:6:122:21 | testComparable11 | testComparable11 |
| interface.go:123:6:123:21 | testComparable12 | testComparable12 |
| interface.go:124:6:124:21 | testComparable13 | testComparable13 |
| interface.go:125:6:125:21 | testComparable14 | testComparable14 |
| interface.go:126:6:126:21 | testComparable15 | testComparable15 |
| interface.go:127:6:127:21 | testComparable16 | testComparable16 |
| interface.go:128:6:128:21 | testComparable17 | testComparable17 |
| interface.go:129:6:129:21 | testComparable18 | testComparable18 |
| interface.go:130:6:130:21 | testComparable19 | testComparable19 |
| interface.go:131:6:131:21 | testComparable20 | testComparable20 |
| interface.go:132:6:132:21 | testComparable21 | testComparable21 |
| interface.go:133:6:133:21 | testComparable22 | testComparable22 |
| interface.go:134:6:134:21 | testComparable23 | testComparable23 |
| interface.go:114:6:114:19 | testComparable | testComparable |
| interface.go:115:6:115:20 | testComparable0 | testComparable0 |
| interface.go:116:6:116:20 | testComparable1 | testComparable1 |
| interface.go:117:6:117:20 | testComparable2 | testComparable2 |
| interface.go:118:6:118:20 | testComparable3 | testComparable3 |
| interface.go:119:6:119:20 | testComparable4 | testComparable4 |
| interface.go:120:6:120:20 | testComparable5 | testComparable5 |
| interface.go:121:6:121:20 | testComparable6 | testComparable6 |
| interface.go:122:6:122:20 | testComparable7 | testComparable7 |
| interface.go:123:6:123:20 | testComparable8 | testComparable8 |
| interface.go:124:6:124:20 | testComparable9 | testComparable9 |
| interface.go:125:6:125:21 | testComparable10 | testComparable10 |
| interface.go:126:6:126:21 | testComparable11 | testComparable11 |
| interface.go:127:6:127:21 | testComparable12 | testComparable12 |
| interface.go:128:6:128:21 | testComparable13 | testComparable13 |
| interface.go:129:6:129:21 | testComparable14 | testComparable14 |
| interface.go:130:6:130:21 | testComparable15 | testComparable15 |
| interface.go:131:6:131:21 | testComparable16 | testComparable16 |
| interface.go:132:6:132:21 | testComparable17 | testComparable17 |
| interface.go:133:6:133:21 | testComparable18 | testComparable18 |
| interface.go:134:6:134:21 | testComparable19 | testComparable19 |
| interface.go:135:6:135:21 | testComparable20 | testComparable20 |
| interface.go:136:6:136:21 | testComparable21 | testComparable21 |
| interface.go:137:6:137:21 | testComparable22 | testComparable22 |
| interface.go:138:6:138:21 | testComparable23 | testComparable23 |
| pkg1/embedding.go:8:6:8:9 | base | base |
| pkg1/embedding.go:19:6:19:13 | embedder | embedder |
| pkg1/embedding.go:22:6:22:16 | ptrembedder | ptrembedder |

View File

@@ -107,28 +107,32 @@ type i20 interface {
StringB() string
}
type testComparable[T comparable] struct{} // $ implementsComparable
type testComparable0[T0 i0] struct{} // $ implementsComparable
type testComparable1[T1 i1] struct{} // $ implementsComparable
type testComparable2[T2 i2] struct{} // $ implementsComparable
type testComparable3[T3 i3] struct{} // $ implementsComparable
type testComparable4[T4 i4] struct{} // $ implementsComparable
type testComparable5[T5 i5] struct{} // does not implement comparable
type testComparable6[T6 i6] struct{} // does not implement comparable
type testComparable7[T7 i7] struct{} // $ implementsComparable
type testComparable8[T8 i8] struct{} // does not implement comparable
type testComparable9[T9 i9] struct{} // does not implement comparable
type testComparable10[T10 i10] struct{} // $ implementsComparable
type testComparable11[T11 i11] struct{} // $ implementsComparable
type testComparable12[T12 i12] struct{} // does not implement comparable
type testComparable13[T13 i13] struct{} // does not implement comparable
type testComparable14[T14 i14] struct{} // $ implementsComparable
type testComparable15[T15 i15] struct{} // $ implementsComparable
type testComparable16[T16 i16] struct{} // does not implement comparable
type testComparable17[T17 i17] struct{} // does not implement comparable
type testComparable18[T18 i18] struct{} // $ implementsComparable
type testComparable19[T19 i19] struct{} // does not implement comparable
type testComparable20[T20 i20] struct{} // $ implementsComparable
type testComparable21[T21 ~[]byte | string] struct{} // does not implement comparable
type testComparable22[T22 any] struct{} // does not implement comparable
type testComparable23[T23 ~[5]byte | string] struct{} // $ implementsComparable
// These used to distinguish strictly-comparable interfaces (i.e. those which will not panic at runtime on attempting a comparison),
// which were required to satisfy the `comparable` type constraint in Go <1.20. Now they all match `comparable` as all interfaces
// are accepted. I mark those which are also strictly comparable for the future in case we want to expose that concept in QL.
type testComparable[T comparable] struct{} // $ implementsComparable isStrictlyComparable
type testComparable0[T0 i0] struct{} // $ implementsComparable isStrictlyComparable
type testComparable1[T1 i1] struct{} // $ implementsComparable isStrictlyComparable
type testComparable2[T2 i2] struct{} // $ implementsComparable isStrictlyComparable
type testComparable3[T3 i3] struct{} // $ implementsComparable isStrictlyComparable
type testComparable4[T4 i4] struct{} // $ implementsComparable isStrictlyComparable
type testComparable5[T5 i5] struct{} // $ implementsComparable
type testComparable6[T6 i6] struct{} // $ implementsComparable
type testComparable7[T7 i7] struct{} // $ implementsComparable isStrictlyComparable
type testComparable8[T8 i8] struct{} // $ implementsComparable
type testComparable9[T9 i9] struct{} // $ implementsComparable
type testComparable10[T10 i10] struct{} // $ implementsComparable isStrictlyComparable
type testComparable11[T11 i11] struct{} // $ implementsComparable isStrictlyComparable
type testComparable12[T12 i12] struct{} // $ implementsComparable
type testComparable13[T13 i13] struct{} // $ implementsComparable
type testComparable14[T14 i14] struct{} // $ implementsComparable isStrictlyComparable
type testComparable15[T15 i15] struct{} // $ implementsComparable isStrictlyComparable
type testComparable16[T16 i16] struct{} // $ implementsComparable
type testComparable17[T17 i17] struct{} // $ implementsComparable
type testComparable18[T18 i18] struct{} // $ implementsComparable isStrictlyComparable
type testComparable19[T19 i19] struct{} // $ implementsComparable
type testComparable20[T20 i20] struct{} // $ implementsComparable isStrictlyComparable
type testComparable21[T21 ~[]byte | string] struct{} // $ implementsComparable
type testComparable22[T22 any] struct{} // $ implementsComparable
type testComparable23[T23 ~[5]byte | string] struct{} // $ implementsComparable isStrictlyComparable

View File

@@ -7,7 +7,7 @@ func testing() {
nonvariadicDeclaredFunction([]int{})
}
func variadicDeclaredFunction(x ...int) int { // $ isVariadic
func variadicDeclaredFunction(x ...int) int {
a := make([]int, 0, 10)
y := append(x, a...)
print(x[0], x[1])
@@ -15,7 +15,7 @@ func variadicDeclaredFunction(x ...int) int { // $ isVariadic
fmt.Fprint(nil, nil, nil)
variadicFunctionLiteral := func(z ...int) int { return z[1] } // $ isVariadic
return variadicFunctionLiteral(y...)
}
} // $ isVariadic
func nonvariadicDeclaredFunction(x []int) int {
return 0

View File

@@ -59,7 +59,7 @@ func main() {
http.HandleFunc("/foo", handler) // $ handler="/foo"
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { // $ handler="/bar"
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
}) // $ handler="/bar"
}

View File

@@ -0,0 +1,58 @@
import go
import TestUtilities.InlineExpectationsTest
class DataConfiguration extends DataFlow::Configuration {
DataConfiguration() { this = "data-configuration" }
override predicate isSource(DataFlow::Node source) {
source = any(DataFlow::CallNode c | c.getCalleeName() = "source").getResult(0)
}
override predicate isSink(DataFlow::Node sink) {
sink = any(DataFlow::CallNode c | c.getCalleeName() = "sink").getArgument(0)
}
}
class DataFlowTest extends InlineExpectationsTest {
DataFlowTest() { this = "DataFlowTest" }
override string getARelevantTag() { result = "dataflow" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "dataflow" and
exists(DataFlow::Node sink | any(DataConfiguration c).hasFlow(_, sink) |
element = sink.toString() and
value = "" and
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
location.getStartColumn(), location.getEndLine(), location.getEndColumn())
)
}
}
class TaintConfiguration extends TaintTracking::Configuration {
TaintConfiguration() { this = "taint-configuration" }
override predicate isSource(DataFlow::Node source) {
source = any(DataFlow::CallNode c | c.getCalleeName() = "source").getResult(0)
}
override predicate isSink(DataFlow::Node sink) {
sink = any(DataFlow::CallNode c | c.getCalleeName() = "sink").getArgument(0)
}
}
class TaintFlowTest extends InlineExpectationsTest {
TaintFlowTest() { this = "TaintFlowTest" }
override string getARelevantTag() { result = "taintflow" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "taintflow" and
exists(DataFlow::Node sink | any(TaintConfiguration c).hasFlow(_, sink) |
element = sink.toString() and
value = "" and
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
location.getStartColumn(), location.getEndLine(), location.getEndColumn())
)
}
}

View File

@@ -0,0 +1,25 @@
package main
func source() string {
return "untrusted data"
}
func sink(string) {
}
func sliceToArray(p []string) [1]string {
return [1]string(p)
}
func main() {
// Test the new slice->array conversion permitted in Go 1.20
var a [4]string
a[0] = source()
alias := sliceToArray(a[:])
sink(alias[0]) // $ taintflow
// Compare with the standard dataflow support for arrays
var b [4]string
b[0] = source()
sink(b[0]) // $ taintflow
}

View File

@@ -316,6 +316,39 @@ func TaintStepTest_BytesReaderWriteTo_B0I0O0(sourceCQL interface{}) interface{}
return intoWriter197
}
func TaintStepTest_Clone(sourceCQL interface{}) interface{} {
fromReader628 := sourceCQL.([]byte)
return bytes.Clone(fromReader628)
}
func TaintStepTest_Cutleft(sourceCQL interface{}) interface{} {
fromReader628 := sourceCQL.([]byte)
sep := []byte{}
left, _, _ := bytes.Cut(fromReader628, sep)
return left
}
func TaintStepTest_Cutright(sourceCQL interface{}) interface{} {
fromReader628 := sourceCQL.([]byte)
sep := []byte{}
_, right, _ := bytes.Cut(fromReader628, sep)
return right
}
func TaintStepTest_CutPrefix(sourceCQL interface{}) interface{} {
fromReader628 := sourceCQL.([]byte)
sep := []byte{}
result, _ := bytes.CutPrefix(fromReader628, sep)
return result
}
func TaintStepTest_CutSuffix(sourceCQL interface{}) interface{} {
fromReader628 := sourceCQL.([]byte)
sep := []byte{}
result, _ := bytes.CutSuffix(fromReader628, sep)
return result
}
func RunAllTaints_Bytes() {
{
source := newSource(0)
@@ -567,4 +600,29 @@ func RunAllTaints_Bytes() {
out := TaintStepTest_BytesReaderWriteTo_B0I0O0(source)
sink(49, out)
}
{
source := newSource(50)
out := TaintStepTest_Cutleft(source)
sink(50, out)
}
{
source := newSource(51)
out := TaintStepTest_Cutright(source)
sink(51, out)
}
{
source := newSource(52)
out := TaintStepTest_CutPrefix(source)
sink(52, out)
}
{
source := newSource(53)
out := TaintStepTest_CutSuffix(source)
sink(53, out)
}
{
source := newSource(54)
out := TaintStepTest_Clone(source)
sink(54, out)
}
}

View File

@@ -23,6 +23,18 @@ func TaintStepTest_ErrorsUnwrap_B0I0O0(sourceCQL interface{}) interface{} {
return intoError957
}
func TaintStepTest_ErrorsJoin1(sourceCQL interface{}) interface{} {
fromError784 := sourceCQL.(error)
intoError957 := errors.Join(fromError784, errors.New(""))
return intoError957
}
func TaintStepTest_ErrorsJoin2(sourceCQL interface{}) interface{} {
fromError784 := sourceCQL.(error)
intoError957 := errors.Join(errors.New(""), fromError784)
return intoError957
}
func RunAllTaints_Errors() {
{
source := newSource(0)
@@ -39,4 +51,14 @@ func RunAllTaints_Errors() {
out := TaintStepTest_ErrorsUnwrap_B0I0O0(source)
sink(2, out)
}
{
source := newSource(3)
out := TaintStepTest_ErrorsJoin1(source)
sink(3, out)
}
{
source := newSource(4)
out := TaintStepTest_ErrorsJoin2(source)
sink(4, out)
}
}

View File

@@ -58,6 +58,30 @@ func TaintStepTest_SyncMapStore_B0I1O0(sourceCQL interface{}) interface{} {
return intoMap881
}
func TaintStepTest_SyncMapSwapinkey(sourceCQL interface{}) interface{} {
var m sync.Map
m.Swap(sourceCQL, "value")
return m
}
func TaintStepTest_SyncMapSwapinvalue(sourceCQL interface{}) interface{} {
var m sync.Map
m.Swap("key", sourceCQL)
return m
}
func TaintStepTest_SyncMapSwapout(sourceCQL interface{}) interface{} {
m := sourceCQL.(sync.Map)
oldVal, _ := m.Swap("key", "value")
return oldVal
}
func TaintStepTest_SyncMapCompareAndSwap(sourceCQL interface{}) interface{} {
var m sync.Map
m.CompareAndSwap("key", "compareTo", sourceCQL)
return m
}
func TaintStepTest_SyncPoolGet_B0I0O0(sourceCQL interface{}) interface{} {
fromPool186 := sourceCQL.(sync.Pool)
intoInterface284 := fromPool186.Get()
@@ -122,4 +146,24 @@ func RunAllTaints_Sync() {
out := TaintStepTest_SyncPoolPut_B0I0O0(source)
sink(9, out)
}
{
source := newSource(10)
out := TaintStepTest_SyncMapSwapinkey(source)
sink(10, out)
}
{
source := newSource(11)
out := TaintStepTest_SyncMapSwapinvalue(source)
sink(11, out)
}
{
source := newSource(12)
out := TaintStepTest_SyncMapSwapout(source)
sink(12, out)
}
{
source := newSource(13)
out := TaintStepTest_SyncMapCompareAndSwap(source)
sink(13, out)
}
}

View File

@@ -0,0 +1,46 @@
package main
import "unsafe"
func TaintStepTest_UnsafeSlice(sourceCQL interface{}) interface{} {
s := sourceCQL.(*byte)
return unsafe.Slice(s, 1)
}
func TaintStepTest_UnsafeSliceData(sourceCQL interface{}) interface{} {
s := sourceCQL.([]byte)
return unsafe.SliceData(s)
}
func TaintStepTest_UnsafeString(sourceCQL interface{}) interface{} {
s := sourceCQL.(*byte)
return unsafe.String(s, 1)
}
func TaintStepTest_UnsafeStringData(sourceCQL interface{}) interface{} {
s := sourceCQL.(string)
return unsafe.StringData(s)
}
func RunAllTaints_Unsafe() {
{
source := newSource(0)
out := TaintStepTest_UnsafeSlice(source)
sink(0, out)
}
{
source := newSource(1)
out := TaintStepTest_UnsafeSliceData(source)
sink(1, out)
}
{
source := newSource(2)
out := TaintStepTest_UnsafeString(source)
sink(2, out)
}
{
source := newSource(3)
out := TaintStepTest_UnsafeStringData(source)
sink(3, out)
}
}

View File

@@ -264,6 +264,9 @@ func testBoundsChecking(input string) {
_ = int16(parsed)
}
}
if parsed <= math.MaxUint16 {
_ = uint16(parsed)
}
}
{
parsed, err := strconv.ParseUint(input, 10, 32)

View File

@@ -0,0 +1,6 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf

View File

@@ -0,0 +1,13 @@
plugins {
/**
* Use `apply false` in the top-level build.gradle file to add a Gradle
* plugin as a build dependency but not apply it to the current (root)
* project. Don't use `apply false` in sub-projects. For more information,
* see Applying external plugins with same version to subprojects.
*/
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
}

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