Merge branch 'main' into deduplicate-dataflow-results-take-3

This commit is contained in:
Mathias Vorreiter Pedersen
2023-09-06 14:12:36 +01:00
255 changed files with 10037 additions and 7657 deletions

View File

@@ -13,7 +13,7 @@ jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Check that implicit this warnings is enabled for all packs
shell: bash
run: |

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ jobs:
qlupgrade:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/fetch-codeql
- name: Check DB upgrade scripts
run: |
@@ -52,7 +52,7 @@ jobs:
matrix:
slice: ["1/2", "2/2"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/fetch-codeql
- uses: ./csharp/actions/create-extractor-pack
- name: Cache compilation cache
@@ -73,7 +73,7 @@ jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup dotnet
uses: actions/setup-dotnet@v3
with:

View File

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

View File

@@ -31,11 +31,11 @@ jobs:
GITHUB_CONTEXT: ${{ toJSON(github.event) }}
run: echo "$GITHUB_CONTEXT"
- name: Clone self (github/codeql) - MERGE
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
path: merge
- name: Clone self (github/codeql) - BASE
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 2
path: base

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,7 +21,7 @@ jobs:
id: go
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql
@@ -56,7 +56,7 @@ jobs:
id: go
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql

View File

@@ -29,7 +29,7 @@ jobs:
id: go
- name: Check out code
uses: actions/checkout@v2
uses: actions/checkout@v4
- name: Set up CodeQL CLI
uses: ./.github/actions/fetch-codeql

View File

@@ -27,7 +27,7 @@ jobs:
name: Test QL
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/fetch-codeql

View File

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

View File

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

View File

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

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest-xl
steps:
### Build the queries ###
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Find codeql

View File

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

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install GNU tar
if: runner.os == 'macOS'
run: |
@@ -113,7 +113,7 @@ jobs:
compile-queries:
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Fetch CodeQL
uses: ./.github/actions/fetch-codeql
- name: Cache compilation cache
@@ -145,7 +145,7 @@ jobs:
runs-on: ubuntu-latest
needs: [build, compile-queries]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/download-artifact@v3
with:
name: ruby.dbscheme
@@ -206,7 +206,7 @@ jobs:
runs-on: ${{ matrix.os }}
needs: [package]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Fetch CodeQL
uses: ./.github/actions/fetch-codeql

View File

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

View File

@@ -33,7 +33,7 @@ jobs:
qlupgrade:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/fetch-codeql
- name: Check DB upgrade scripts
run: |
@@ -54,7 +54,7 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack
- name: Cache compilation cache

View File

@@ -39,31 +39,31 @@ jobs:
build-and-test-macos:
runs-on: macos-12-xl
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./swift/actions/build-and-test
build-and-test-linux:
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./swift/actions/build-and-test
qltests-linux:
needs: build-and-test-linux
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./swift/actions/run-ql-tests
qltests-macos:
if : ${{ github.event_name == 'pull_request' }}
needs: build-and-test-macos
runs-on: macos-12-xl
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./swift/actions/run-ql-tests
integration-tests-linux:
needs: build-and-test-linux
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./swift/actions/run-integration-tests
integration-tests-macos:
if : ${{ github.event_name == 'pull_request' }}
@@ -71,13 +71,13 @@ jobs:
runs-on: macos-12-xl
timeout-minutes: 60
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./swift/actions/run-integration-tests
codegen:
if : ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: bazelbuild/setup-bazelisk@v2
- uses: actions/setup-python@v4
with:
@@ -102,6 +102,6 @@ jobs:
if : ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: ./.github/actions/fetch-codeql
- uses: ./swift/actions/database-upgrade-scripts

View File

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

View File

@@ -27,7 +27,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run tests
@@ -35,12 +35,12 @@ jobs:
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Check formatting
run: cargo fmt --check
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Run clippy
run: cargo clippy -- --no-deps -D warnings -A clippy::new_without_default -A clippy::too_many_arguments

View File

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

View File

@@ -447,9 +447,16 @@ class GlobalUse extends UseImpl, TGlobalUse {
IRFunction getIRFunction() { result = f }
final override predicate hasIndexInBlock(IRBlock block, int index) {
exists(ExitFunctionInstruction exit |
exit = f.getExitFunctionInstruction() and
block.getInstruction(index) = exit
// Similar to the `FinalParameterUse` case, we want to generate flow out of
// globals at any exit so that we can flow out of non-returning functions.
// Obviously this isn't correct as we can't actually flow but the global flow
// requires this if we want to flow into children.
exists(Instruction return |
return instanceof ReturnInstruction or
return instanceof UnreachedInstruction
|
block.getInstruction(index) = return and
return.getEnclosingIRFunction() = f
)
}

View File

@@ -1,12 +1,21 @@
edges
| test.c:14:27:14:30 | argv indirection | test.c:21:18:21:23 | query1 indirection |
| test.c:14:27:14:30 | argv indirection | test.c:35:16:35:23 | userName indirection |
| test.c:35:16:35:23 | userName indirection | test.c:40:25:40:32 | username indirection |
| test.c:38:7:38:20 | globalUsername indirection | test.c:51:18:51:23 | query1 indirection |
| test.c:40:25:40:32 | username indirection | test.c:38:7:38:20 | globalUsername indirection |
| test.cpp:39:27:39:30 | argv indirection | test.cpp:43:27:43:33 | access to array indirection |
nodes
| test.c:14:27:14:30 | argv indirection | semmle.label | argv indirection |
| test.c:21:18:21:23 | query1 indirection | semmle.label | query1 indirection |
| test.c:35:16:35:23 | userName indirection | semmle.label | userName indirection |
| test.c:38:7:38:20 | globalUsername indirection | semmle.label | globalUsername indirection |
| test.c:40:25:40:32 | username indirection | semmle.label | username indirection |
| test.c:51:18:51:23 | query1 indirection | semmle.label | query1 indirection |
| test.cpp:39:27:39:30 | argv indirection | semmle.label | argv indirection |
| test.cpp:43:27:43:33 | access to array indirection | semmle.label | access to array indirection |
subpaths
#select
| test.c:21:18:21:23 | query1 | test.c:14:27:14:30 | argv indirection | test.c:21:18:21:23 | query1 indirection | This argument to a SQL query function is derived from $@ and then passed to mysql_query(sqlArg). | test.c:14:27:14:30 | argv indirection | user input (a command-line argument) |
| test.c:51:18:51:23 | query1 | test.c:14:27:14:30 | argv indirection | test.c:51:18:51:23 | query1 indirection | This argument to a SQL query function is derived from $@ and then passed to mysql_query(sqlArg). | test.c:14:27:14:30 | argv indirection | user input (a command-line argument) |
| test.cpp:43:27:43:33 | access to array | test.cpp:39:27:39:30 | argv indirection | test.cpp:43:27:43:33 | access to array indirection | This argument to a SQL query function is derived from $@ and then passed to pqxx::work::exec1((unnamed parameter 0)). | test.cpp:39:27:39:30 | argv indirection | user input (a command-line argument) |

View File

@@ -8,7 +8,7 @@ int snprintf(char *s, size_t n, const char *format, ...);
void sanitizeString(char *stringOut, size_t len, const char *strIn);
int mysql_query(int arg1, const char *sqlArg);
int atoi(const char *nptr);
void exit(int i);
///// Test code /////
int main(int argc, char** argv) {
@@ -31,4 +31,22 @@ int main(int argc, char** argv) {
char query3[1000] = {0};
snprintf(query3, 1000, "SELECT UID FROM USERS where number = \"%i\"", userNumber);
mysql_query(0, query3); // GOOD
nonReturning(userName);
}
char* globalUsername;
void nonReturning(char* username) {
globalUsername = username;
badFunc();
// This function does not return, so we used to lose the global flow here.
exit(0);
}
void badFunc() {
char *userName = globalUsername;
char query1[1000] = {0};
snprintf(query1, 1000, "SELECT UID FROM USERS where name = \"%s\"", userName);
mysql_query(0, query1); // BAD
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Update the schemas for some tables to disallow assembly locations.
compatibility: full

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Util;
namespace Semmle.Extraction.CSharp.Entities
{
@@ -82,8 +84,15 @@ namespace Semmle.Extraction.CSharp.Entities
var baseTypes = GetBaseTypeDeclarations();
var hasExpandingCycle = GenericsRecursionGraph.HasExpandingCycle(Symbol);
if (hasExpandingCycle)
{
Context.ExtractionError("Found recursive generic inheritance hierarchy. Base class of type is not extracted", Symbol.ToDisplayString(), Context.CreateLocation(ReportingLocation), severity: Util.Logging.Severity.Warning);
}
// Visit base types
if (Symbol.GetNonObjectBaseType(Context) is INamedTypeSymbol @base)
if (!hasExpandingCycle
&& Symbol.GetNonObjectBaseType(Context) is INamedTypeSymbol @base)
{
var bts = GetBaseTypeDeclarations(baseTypes, @base);
@@ -347,6 +356,197 @@ namespace Semmle.Extraction.CSharp.Entities
}
public override int GetHashCode() => SymbolEqualityComparer.Default.GetHashCode(Symbol);
/// <summary>
/// Class to detect recursive generic inheritance hierarchies.
///
/// Details can be found in https://www.ecma-international.org/wp-content/uploads/ECMA-335_6th_edition_june_2012.pdf Chapter II.9.2 Generics and recursive inheritance graphs
/// The dotnet runtime already implements this check as a runtime validation: https://github.com/dotnet/runtime/blob/e48e88d0fe9c2e494c0e6fd0c7c1fb54e7ddbdb1/src/coreclr/vm/generics.cpp#L748
/// </summary>
private class GenericsRecursionGraph
{
private static readonly ConcurrentDictionary<INamedTypeSymbol, bool> resultCache = new(SymbolEqualityComparer.Default);
/// <summary>
/// Checks whether the given type has a recursive generic inheritance hierarchy. The result is cached.
/// </summary>
public static bool HasExpandingCycle(ITypeSymbol start)
{
if (start.OriginalDefinition is not INamedTypeSymbol namedTypeDefinition ||
!namedTypeDefinition.IsGenericType)
{
return false;
}
return resultCache.GetOrAdd(namedTypeDefinition, nt => new GenericsRecursionGraph(nt).HasExpandingCycle());
}
private readonly INamedTypeSymbol startSymbol;
private readonly HashSet<INamedTypeSymbol> instantiationClosure = new(SymbolEqualityComparer.Default);
private readonly Dictionary<ITypeParameterSymbol, List<(ITypeParameterSymbol To, bool IsExpanding)>> edges = new(SymbolEqualityComparer.Default);
private GenericsRecursionGraph(INamedTypeSymbol startSymbol)
{
this.startSymbol = startSymbol;
ComputeInstantiationClosure();
ComputeGraphEdges();
}
private void ComputeGraphEdges()
{
foreach (var reference in instantiationClosure)
{
var definition = reference.OriginalDefinition;
if (SymbolEqualityComparer.Default.Equals(reference, definition))
{
// It's a definition, so no edges
continue;
}
for (var i = 0; i < reference.TypeArguments.Length; i++)
{
var target = definition.TypeParameters[i];
if (reference.TypeArguments[i] is ITypeParameterSymbol source)
{
// non-expanding
edges.AddAnother(source, (target, false));
}
else if (reference.TypeArguments[i] is INamedTypeSymbol namedType)
{
// expanding
var sources = GetAllNestedTypeParameters(namedType);
foreach (var s in sources)
{
edges.AddAnother(s, (target, true));
}
}
}
}
}
private static List<ITypeParameterSymbol> GetAllNestedTypeParameters(INamedTypeSymbol symbol)
{
var res = new List<ITypeParameterSymbol>();
void AddTypeParameters(INamedTypeSymbol symbol)
{
foreach (var typeArgument in symbol.TypeArguments)
{
if (typeArgument is ITypeParameterSymbol typeParameter)
{
res.Add(typeParameter);
}
else if (typeArgument is INamedTypeSymbol namedType)
{
AddTypeParameters(namedType);
}
}
}
AddTypeParameters(symbol);
return res;
}
private void ComputeInstantiationClosure()
{
var workQueue = new Queue<INamedTypeSymbol>();
workQueue.Enqueue(startSymbol);
while (workQueue.Count > 0)
{
var current = workQueue.Dequeue();
if (instantiationClosure.Contains(current) ||
!current.IsGenericType)
{
continue;
}
instantiationClosure.Add(current);
if (SymbolEqualityComparer.Default.Equals(current, current.OriginalDefinition))
{
// Definition, so enqueue all base types and interfaces
if (current.BaseType != null)
{
workQueue.Enqueue(current.BaseType);
}
foreach (var i in current.Interfaces)
{
workQueue.Enqueue(i);
}
}
else
{
// Reference, so enqueue all type arguments and their original definitions:
foreach (var namedTypeArgument in current.TypeArguments.OfType<INamedTypeSymbol>())
{
workQueue.Enqueue(namedTypeArgument);
workQueue.Enqueue(namedTypeArgument.OriginalDefinition);
}
}
}
}
private bool HasExpandingCycle()
{
return startSymbol.TypeParameters.Any(HasExpandingCycle);
}
private bool HasExpandingCycle(ITypeParameterSymbol start)
{
var visited = new HashSet<ITypeParameterSymbol>(SymbolEqualityComparer.Default);
var path = new List<ITypeParameterSymbol>();
var hasExpandingCycle = HasExpandingCycle(start, visited, path, hasSeenExpandingEdge: false);
return hasExpandingCycle;
}
private List<(ITypeParameterSymbol To, bool IsExpanding)> GetOutgoingEdges(ITypeParameterSymbol typeParameter)
{
return edges.TryGetValue(typeParameter, out var outgoingEdges)
? outgoingEdges
: new List<(ITypeParameterSymbol, bool)>();
}
/// <summary>
/// A modified cycle detection algorithm based on DFS.
/// </summary>
/// <param name="current">The current node that is being visited</param>
/// <param name="visited">The nodes that have already been visited by any path.</param>
/// <param name="currentPath">The nodes already visited on the current path.</param>
/// <param name="hasSeenExpandingEdge">Whether an expanding edge was already seen in this path. We're looking for a cycle that has at least one expanding edge.</param>
/// <returns></returns>
private bool HasExpandingCycle(ITypeParameterSymbol current, HashSet<ITypeParameterSymbol> visited, List<ITypeParameterSymbol> currentPath, bool hasSeenExpandingEdge)
{
if (currentPath.Count > 0 && SymbolEqualityComparer.Default.Equals(current, currentPath[0]))
{
return hasSeenExpandingEdge;
}
if (visited.Contains(current))
{
return false;
}
visited.Add(current);
currentPath.Add(current);
var outgoingEdges = GetOutgoingEdges(current);
foreach (var outgoingEdge in outgoingEdges)
{
if (HasExpandingCycle(outgoingEdge.To, visited, currentPath, hasSeenExpandingEdge: hasSeenExpandingEdge || outgoingEdge.IsExpanding))
{
return true;
}
}
currentPath.RemoveAt(currentPath.Count - 1);
return false;
}
}
}
internal abstract class Type<T> : Type where T : ITypeSymbol

View File

@@ -0,0 +1,4 @@
| test.cs:2:14:2:20 | Found recursive generic inheritance hierarchy. Base class of type is not extracted | 4 | GenB<GenB<T>> |
| test.cs:2:14:2:20 | Found recursive generic inheritance hierarchy. Base class of type is not extracted | 4 | GenB<GenB<string>> |
| test.cs:2:14:2:20 | Found recursive generic inheritance hierarchy. Base class of type is not extracted | 4 | GenB<T> |
| test.cs:2:14:2:20 | Found recursive generic inheritance hierarchy. Base class of type is not extracted | 4 | GenB<string> |

View File

@@ -0,0 +1,6 @@
import csharp
import semmle.code.csharp.commons.Diagnostics
query predicate extractorMessages(ExtractorMessage msg, int severity, string elementText) {
msg.getSeverity() = severity and msg.getElementText() = elementText
}

View File

@@ -0,0 +1,35 @@
| test.cs:1:14:1:20 | GenA<> | System.Object |
| test.cs:1:14:1:20 | GenA<GenB<GenB<>>> | System.Object |
| test.cs:1:14:1:20 | GenA<GenB<GenB<String>>> | System.Object |
| test.cs:2:14:2:20 | GenB<> | System.Object |
| test.cs:2:14:2:20 | GenB<GenB<>> | System.Object |
| test.cs:2:14:2:20 | GenB<GenB<String>> | System.Object |
| test.cs:2:14:2:20 | GenB<String> | System.Object |
| test.cs:4:7:4:10 | P<> | System.Object |
| test.cs:4:7:4:10 | P<C<,>> | System.Object |
| test.cs:4:7:4:10 | P<C<Int32,String>> | System.Object |
| test.cs:4:7:4:10 | P<C<String,Int32>> | System.Object |
| test.cs:4:7:4:10 | P<C<V,U>> | System.Object |
| test.cs:4:7:4:10 | P<C<W,X>> | System.Object |
| test.cs:4:7:4:10 | P<C<X,W>> | System.Object |
| test.cs:4:7:4:10 | P<D<,>> | System.Object |
| test.cs:4:7:4:10 | P<D<Int32,String>> | System.Object |
| test.cs:4:7:4:10 | P<D<String,Int32>> | System.Object |
| test.cs:4:7:4:10 | P<D<U,V>> | System.Object |
| test.cs:4:7:4:10 | P<D<V,U>> | System.Object |
| test.cs:4:7:4:10 | P<D<X,W>> | System.Object |
| test.cs:5:7:5:13 | C<,> | P<D<V,U>> |
| test.cs:5:7:5:13 | C<Int32,String> | P<D<System.String,System.Int32>> |
| test.cs:5:7:5:13 | C<String,Int32> | P<D<System.Int32,System.String>> |
| test.cs:5:7:5:13 | C<V,U> | P<D<U,V>> |
| test.cs:5:7:5:13 | C<W,X> | P<D<X,W>> |
| test.cs:5:7:5:13 | C<X,W> | P<D<,>> |
| test.cs:6:7:6:13 | D<,> | P<C<W,X>> |
| test.cs:6:7:6:13 | D<Int32,String> | P<C<System.Int32,System.String>> |
| test.cs:6:7:6:13 | D<String,Int32> | P<C<System.String,System.Int32>> |
| test.cs:6:7:6:13 | D<U,V> | P<C<,>> |
| test.cs:6:7:6:13 | D<V,U> | P<C<V,U>> |
| test.cs:6:7:6:13 | D<X,W> | P<C<X,W>> |
| test.cs:8:7:8:10 | A<> | System.Object |
| test.cs:8:7:8:10 | A<String> | System.Object |
| test.cs:13:14:13:18 | Class | System.Object |

View File

@@ -0,0 +1,5 @@
import csharp
from Class c
where c.fromSource()
select c, c.getBaseClass().getQualifiedName()

View File

@@ -0,0 +1,23 @@
public class GenA<U> { };
public class GenB<T> : GenA<GenB<GenB<T>>> { };
class P<T> { }
class C<U, V> : P<D<V, U>> { }
class D<W, X> : P<C<W, X>> { }
class A<T> : System.IEquatable<A<T>>
{
public bool Equals(A<T> other) { return true; }
}
public class Class
{
public static int Main()
{
GenB<string> a = new GenB<string>();
P<D<string, int>> b = new C<int, string>();
A<string> c = new A<string>();
return 0;
}
}

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<Target Name="DeleteBinObjFolders" BeforeTargets="Clean">
<RemoveDir Directories=".\bin" />
<RemoveDir Directories=".\obj" />
</Target>
</Project>

View File

@@ -0,0 +1,3 @@
from create_database_utils import *
run_codeql_database_create(['dotnet build'], lang="csharp", extra_args=["--extractor-option=cil=false"])

View File

@@ -1165,8 +1165,14 @@ private module ArgumentNodes {
) {
e1.(Argument).isArgumentOf(e2, _) and
exactScope = false and
scope = e2 and
isSuccessor = true
isSuccessor = true and
if e2 instanceof PropertyWrite
then
exists(AssignableDefinition def |
def.getTargetAccess() = e2 and
scope = def.getExpr()
)
else scope = e2
}
}

View File

@@ -136,7 +136,7 @@ diagnostics(
string error_tag: string ref,
string error_message: string ref,
string full_error_message: string ref,
int location: @location_default ref
int location: @location ref
);
extractor_messages(
@@ -145,7 +145,7 @@ extractor_messages(
string origin : string ref,
string text : string ref,
string entity : string ref,
int location: @location_default ref,
int location: @location ref,
string stack_trace : string ref
);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Update the schemas for some tables to allow assembly locations.
compatibility: full

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added [http.Error](https://pkg.go.dev/net/http#Error) to XSS sanitzers.

View File

@@ -109,6 +109,18 @@ module SharedXss {
}
}
/**
* A http.Error function returns with the ContentType of text/plain, and is not a valid XSS sink
*/
class ErrorSanitizer extends Sanitizer {
ErrorSanitizer() {
exists(Function f, DataFlow::CallNode call | call = f.getACall() |
f.hasQualifiedName("net/http", "Error") and
call.getArgument(1) = this
)
}
}
/**
* A regexp replacement involving an HTML meta-character, or a call to an escape
* function, viewed as a sanitizer for XSS vulnerabilities.

View File

@@ -25,9 +25,9 @@ func ServeJsonDirect(w http.ResponseWriter, r http.Request) {
func ErrTest(w http.ResponseWriter, r http.Request) {
cookie, err := r.Cookie("somecookie")
w.Write([]byte(fmt.Sprintf("Cookie result: %v", cookie))) // BAD: Cookie's value is user-controlled
w.Write([]byte(fmt.Sprintf("Cookie check error: %v", err))) // GOOD: Cookie's err return is harmless
w.Write([]byte(fmt.Sprintf("Cookie result: %v", cookie))) // BAD: Cookie's value is user-controlled
w.Write([]byte(fmt.Sprintf("Cookie check error: %v", err))) // GOOD: Cookie's err return is harmless
http.Error(w, fmt.Sprintf("Cookie result: %v", cookie), 500) // Good: only plain text is written.
file, header, err := r.FormFile("someFile")
content, err2 := ioutil.ReadAll(file)
w.Write([]byte(fmt.Sprintf("File content: %v", content))) // BAD: file content is user-controlled

View File

@@ -92,6 +92,7 @@ def compile_to_dir(build_dir, srcs, classpath, java_classpath, output):
kotlin_arg_file = build_dir + '/kotlin.args'
kotlin_args = ['-Werror',
'-opt-in=kotlin.RequiresOptIn',
'-opt-in=org.jetbrains.kotlin.ir.symbols.IrSymbolInternals',
'-d', output,
'-module-name', 'codeql-kotlin-extractor',
'-no-reflect', '-no-stdlib',
@@ -168,7 +169,7 @@ def compile(jars, java_jars, dependency_folder, transform_to_embeddable, output,
shutil.rmtree(tmp_src_dir)
shutil.copytree('src', tmp_src_dir)
include_version_folder = tmp_src_dir + '/main/kotlin/utils/versions/to_include'
include_version_folder = tmp_src_dir + '/main/kotlin/utils/this_version'
os.makedirs(include_version_folder)
resource_dir = tmp_src_dir + '/main/resources/com/github/codeql'
@@ -192,11 +193,7 @@ def compile(jars, java_jars, dependency_folder, transform_to_embeddable, output,
shutil.copytree(d, include_version_folder, dirs_exist_ok=True)
# remove all version folders:
for version in kotlin_plugin_versions.many_versions:
d = tmp_src_dir + '/main/kotlin/utils/versions/v_' + \
version.replace('.', '_')
if os.path.exists(d):
shutil.rmtree(d)
shutil.rmtree(tmp_src_dir + '/main/kotlin/utils/versions')
srcs = find_sources(tmp_src_dir)

View File

@@ -20,6 +20,7 @@ import java.nio.file.Files
import java.nio.file.Paths
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
import com.github.codeql.utils.versions.usesK2
import com.semmle.util.files.FileUtil
import kotlin.system.exitProcess
@@ -97,6 +98,7 @@ class KotlinExtractorExtension(
private fun runExtractor(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
val startTimeMs = System.currentTimeMillis()
val usesK2 = usesK2(pluginContext)
// This default should be kept in sync with com.semmle.extractor.java.interceptors.KotlinInterceptor.initializeExtractionContext
val trapDir = File(System.getenv("CODEQL_EXTRACTOR_JAVA_TRAP_DIR").takeUnless { it.isNullOrEmpty() } ?: "kotlin-extractor/trap")
val compression_env_var = "CODEQL_EXTRACTOR_JAVA_OPTION_TRAP_COMPRESSION"
@@ -134,6 +136,7 @@ class KotlinExtractorExtension(
tw.writeCompilation_info(compilation, "Kotlin Compiler Version", KotlinCompilerVersion.getVersion() ?: "<unknown>")
val extractor_name = this::class.java.getResource("extractor.name")?.readText() ?: "<unknown>"
tw.writeCompilation_info(compilation, "Kotlin Extractor Name", extractor_name)
tw.writeCompilation_info(compilation, "Uses Kotlin 2", usesK2.toString())
if (compilationStartTime != null) {
tw.writeCompilation_compiler_times(compilation, -1.0, (System.currentTimeMillis()-compilationStartTime)/1000.0)
}

View File

@@ -107,7 +107,7 @@ open class KotlinFileExtractor(
fun extractFileContents(file: IrFile, id: Label<DbFile>) {
with("file", file) {
val locId = tw.getWholeFileLocation()
val pkg = file.fqName.asString()
val pkg = file.packageFqName.asString()
val pkgId = extractPackage(pkg)
tw.writeHasLocation(id, locId)
tw.writeCupackage(id, pkgId)
@@ -1901,8 +1901,9 @@ open class KotlinFileExtractor(
verboseln("No match as didn't find target package")
return false
}
if (targetPkg.fqName.asString() != pName) {
verboseln("No match as package name is ${targetPkg.fqName.asString()}")
val targetName = targetPkg.packageFqName.asString()
if (targetName != pName) {
verboseln("No match as package name is $targetName")
return false
}
verboseln("Match")
@@ -2556,8 +2557,9 @@ open class KotlinFileExtractor(
verboseln("No match as didn't find target package")
return false
}
if (targetPkg.fqName.asString() != pkgName) {
verboseln("No match as package name is ${targetPkg.fqName.asString()} not $pkgName")
val targetName = targetPkg.packageFqName.asString()
if (targetName != pkgName) {
verboseln("No match as package name is $targetName not $pkgName")
return false
}
verboseln("Match")

View File

@@ -5,6 +5,7 @@ import com.github.codeql.utils.versions.codeQlWithHasQuestionMark
import com.github.codeql.utils.versions.getFileClassFqName
import com.github.codeql.utils.versions.getKotlinType
import com.github.codeql.utils.versions.isRawType
import com.github.codeql.utils.versions.packageFqName
import com.semmle.extractor.java.OdasaOutput
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.ir.*
@@ -83,7 +84,7 @@ open class KotlinUsesExtractor(
)
fun extractFileClass(f: IrFile): Label<out DbClassorinterface> {
val pkg = f.fqName.asString()
val pkg = f.packageFqName.asString()
val jvmName = getFileClassName(f)
val id = extractFileClass(pkg, jvmName)
if (tw.lm.fileClassLocationsExtracted.add(f)) {
@@ -848,7 +849,7 @@ open class KotlinUsesExtractor(
when(dp) {
is IrFile ->
if(canBeTopLevel) {
usePackage(dp.fqName.asString())
usePackage(dp.packageFqName.asString())
} else {
extractFileClass(dp)
}

View File

@@ -276,8 +276,8 @@ class MetaAnnotationSupport(private val logger: FileLogger, private val pluginCo
run { logger.warnElement("Expected property's parent class to have a receiver parameter", parentClass); return }
val newParam = copyParameterToFunction(thisReceiever, this)
dispatchReceiverParameter = newParam
body = factory.createBlockBody(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, listOf(
body = factory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET).apply({
this.statements.add(
IrReturnImpl(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
pluginContext.irBuiltIns.nothingType,
@@ -294,7 +294,7 @@ class MetaAnnotationSupport(private val logger: FileLogger, private val pluginCo
)
)
)
)
})
}
}

View File

@@ -8,11 +8,12 @@ import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.name.FqName
import com.github.codeql.utils.*
import com.github.codeql.utils.versions.packageFqName
class PrimitiveTypeMapping(val logger: Logger, val pluginContext: IrPluginContext) {
fun getPrimitiveInfo(s: IrSimpleType) =
s.classOrNull?.let {
if ((it.owner.parent as? IrPackageFragment)?.fqName == StandardNames.BUILT_INS_PACKAGE_FQ_NAME)
if ((it.owner.parent as? IrPackageFragment)?.packageFqName == StandardNames.BUILT_INS_PACKAGE_FQ_NAME)
mapping[it.owner.name]
else
null

View File

@@ -3,8 +3,8 @@ package com.github.codeql.comments
import com.github.codeql.*
import com.github.codeql.utils.IrVisitorLookup
import com.github.codeql.utils.isLocalFunction
import com.github.codeql.utils.Psi2IrFacade
import com.github.codeql.utils.versions.getPsi2Ir
import com.github.codeql.utils.versions.Psi2IrFacade
import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.KotlinCompilerVersion

View File

@@ -2,6 +2,7 @@ package com.github.codeql
import com.github.codeql.utils.getJvmName
import com.github.codeql.utils.versions.getFileClassFqName
import com.github.codeql.utils.versions.packageFqName
import com.intellij.openapi.vfs.StandardFileSystems
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
@@ -33,7 +34,7 @@ fun getFileClassName(f: IrFile) =
fun getIrElementBinaryName(that: IrElement): String {
if (that is IrFile) {
val shortName = getFileClassName(that)
val pkg = that.fqName.asString()
val pkg = that.packageFqName.asString()
return if (pkg.isEmpty()) shortName else "$pkg.$shortName"
}
@@ -59,7 +60,7 @@ fun getIrElementBinaryName(that: IrElement): String {
.forEach {
when (it) {
is IrClass -> internalName.insert(0, getName(it) + "$")
is IrPackageFragment -> it.fqName.asString().takeIf { fqName -> fqName.isNotEmpty() }?.let { fqName -> internalName.insert(0, "$fqName.") }
is IrPackageFragment -> it.packageFqName.asString().takeIf { fqName -> fqName.isNotEmpty() }?.let { fqName -> internalName.insert(0, "$fqName.") }
}
}
return internalName.toString()

View File

@@ -1,6 +1,6 @@
package com.github.codeql.utils
import com.github.codeql.utils.versions.Psi2IrFacade
import com.github.codeql.utils.Psi2IrFacade
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrDeclaration

View File

@@ -1,4 +1,4 @@
package com.github.codeql.utils.versions
package com.github.codeql.utils
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.ir.IrElement
@@ -8,4 +8,4 @@ import org.jetbrains.kotlin.psi.KtFile
interface Psi2IrFacade {
fun getKtFile(irFile: IrFile): KtFile?
fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement?
}
}

View File

@@ -0,0 +1,4 @@
package org.jetbrains.kotlin.ir.symbols
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
annotation class IrSymbolInternals

View File

@@ -1,3 +1,5 @@
package com.github.codeql.utils.versions
import com.github.codeql.utils.Psi2IrFacade
fun getPsi2Ir(): Psi2IrFacade? = null

View File

@@ -0,0 +1,7 @@
package com.github.codeql.utils.versions
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
fun usesK2(@Suppress("UNUSED_PARAMETER") pluginContext: IrPluginContext): Boolean {
return false
}

View File

@@ -0,0 +1,10 @@
package com.github.codeql.utils.versions
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.name.FqName
val IrFile.packageFqName: FqName
get() = this.fqName
val IrPackageFragment.packageFqName: FqName
get() = this.fqName

View File

@@ -6,6 +6,7 @@ import org.jetbrains.kotlin.backend.jvm.ir.getKtFile
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.psi.KtFile
import com.github.codeql.utils.Psi2IrFacade
fun getPsi2Ir(): Psi2IrFacade? = Psi2Ir()

View File

@@ -0,0 +1,7 @@
package com.github.codeql.utils.versions
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
fun usesK2(pluginContext: IrPluginContext): Boolean {
return pluginContext.languageVersionSettings.languageVersion.usesK2
}

View File

@@ -0,0 +1,4 @@
package com.github.codeql
// The compiler provides the annotation class, so we don't need to do
// anything

View File

@@ -0,0 +1,3 @@
package com.github.codeql
// The compiler provides packageFqName, so we don't need to do anything

View File

@@ -0,0 +1,7 @@
| Number of files with extension jar | 1 |
| Number of files with extension kt | 1 |
| Number of lines of code | 2 |
| Number of lines of code with extension kt | 2 |
| Total number of lines | 3 |
| Total number of lines with extension kt | 3 |
| Uses Kotlin 2: false | 1 |

View File

@@ -0,0 +1,13 @@
extensions:
- addsTo:
pack: codeql/java-queries
extensible: extractorInformationSkipKey
data:
# These will have unstable values, as they are dependent on the
# JDK that the test is run with, so filter them out:
- ["Number of files"]
- ["Number of files with extension class"]
# These depend on the Kotlin version you have installed
- ["Kotlin Compiler Version: %"]
- ["Kotlin Extractor Name: %"]

View File

@@ -0,0 +1 @@
Telemetry/ExtractorInformation.ql

View File

@@ -0,0 +1,2 @@
class SomeClass {
}

View File

@@ -0,0 +1,3 @@
from create_database_utils import *
run_codeql_database_create(["kotlinc SomeClass.kt"], lang="java")

View File

@@ -0,0 +1,3 @@
| Number of files with extension jar | 1 |
| Number of files with extension kt | 1 |
| Uses Kotlin 2: true | 1 |

View File

@@ -0,0 +1,17 @@
extensions:
- addsTo:
pack: codeql/java-queries
extensible: extractorInformationSkipKey
data:
# These will have unstable values, as they are dependent on the
# JDK that the test is run with, so filter them out:
- ["Number of files"]
- ["Number of files with extension class"]
# These depend on the Kotlin version you have installed
- ["Kotlin Compiler Version: %"]
- ["Kotlin Extractor Name: %"]
# Currently we get some diagnostics as Kotlin 2 support isn't complete.
# Ignore these, as the number probably isn't stable.
- ["Number of diagnostics from CodeQL Kotlin extractor with severity %"]
- ["Total number of diagnostics from CodeQL Kotlin extractor"]

View File

@@ -0,0 +1 @@
Telemetry/ExtractorInformation.ql

View File

@@ -0,0 +1,2 @@
class SomeClass {
}

View File

@@ -0,0 +1,3 @@
from create_database_utils import *
run_codeql_database_create(["kotlinc -language-version 2.0 SomeClass.kt"], lang="java")

View File

@@ -87,7 +87,7 @@ predicate extractorTotalDiagnostics(string key, int value) {
from string key, int value
where
not extractorInformationSkipKey(key) and
not exists(string pattern | extractorInformationSkipKey(pattern) and key.matches(pattern)) and
(
compilationInfo(key, value) or
fileCount(key, value) or

View File

@@ -1 +0,0 @@
ERROR: class or primitive expected in query (selectAtType.ql:5,8-15)

View File

@@ -1,5 +0,0 @@
import java
@classorinterface clasz() { any() }
select clasz()

View File

@@ -114,7 +114,7 @@ def make_patches_from_log_file(log_file_lines) -> List[Patch]:
while True:
next_line = parse_log_line(next(lines))
# it can be the case that
if next_line[0] in (" ", "-", "+", "@"):
if next_line and next_line[0] in (" ", "-", "+", "@"):
lines_changed.append(next_line)
if "FAILED" in next_line:
break

View File

@@ -16,9 +16,11 @@ private import semmle.python.dataflow.new.SensitiveDataSources
import CleartextLoggingCustomizations::CleartextLogging
/**
* DEPRECATED: Use `CleartextLoggingFlow` module instead.
*
* A taint-tracking configuration for detecting "Clear-text logging of sensitive information".
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "CleartextLogging" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -31,3 +33,14 @@ class Configuration extends TaintTracking::Configuration {
node instanceof Sanitizer
}
}
private module CleartextLoggingConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "Clear-text logging of sensitive information" vulnerabilities. */
module CleartextLoggingFlow = TaintTracking::Global<CleartextLoggingConfig>;

View File

@@ -16,9 +16,11 @@ private import semmle.python.dataflow.new.SensitiveDataSources
import CleartextStorageCustomizations::CleartextStorage
/**
* DEPRECATED: Use `CleartextStorageFlow` module instead.
*
* A taint-tracking configuration for detecting "Clear-text storage of sensitive information".
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "CleartextStorage" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -31,3 +33,14 @@ class Configuration extends TaintTracking::Configuration {
node instanceof Sanitizer
}
}
private module CleartextStorageConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "Clear-text storage of sensitive information" vulnerabilities. */
module CleartextStorageFlow = TaintTracking::Global<CleartextStorageConfig>;

View File

@@ -12,9 +12,11 @@ import semmle.python.dataflow.new.TaintTracking
import CodeInjectionCustomizations::CodeInjection
/**
* DEPRECATED: Use `CodeInjectionFlow` module instead.
*
* A taint-tracking configuration for detecting "code injection" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "CodeInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -27,3 +29,14 @@ class Configuration extends TaintTracking::Configuration {
guard instanceof SanitizerGuard
}
}
private module CodeInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "code injection" vulnerabilities. */
module CodeInjectionFlow = TaintTracking::Global<CodeInjectionConfig>;

View File

@@ -12,9 +12,11 @@ import semmle.python.dataflow.new.TaintTracking
import CommandInjectionCustomizations::CommandInjection
/**
* DEPRECATED: Use `CommandInjectionFlow` module instead.
*
* A taint-tracking configuration for detecting "command injection" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "CommandInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -27,3 +29,17 @@ class Configuration extends TaintTracking::Configuration {
guard instanceof SanitizerGuard
}
}
/**
* A taint-tracking configuration for detecting "command injection" vulnerabilities.
*/
module CommandInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "command injection" vulnerabilities. */
module CommandInjectionFlow = TaintTracking::Global<CommandInjectionConfig>;

View File

@@ -14,10 +14,12 @@ import semmle.python.dataflow.new.RemoteFlowSources
import LdapInjectionCustomizations::LdapInjection
/**
* DEPRECATED: Use `LdapInjectionDnFlow` module instead.
*
* A taint-tracking configuration for detecting LDAP injection vulnerabilities
* via the distinguished name (DN) parameter of an LDAP search.
*/
class DnConfiguration extends TaintTracking::Configuration {
deprecated class DnConfiguration extends TaintTracking::Configuration {
DnConfiguration() { this = "LdapDnInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -31,11 +33,24 @@ class DnConfiguration extends TaintTracking::Configuration {
}
}
private module LdapInjectionDnConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof DnSink }
predicate isBarrier(DataFlow::Node node) { node instanceof DnSanitizer }
}
/** Global taint-tracking for detecting "LDAP injection via the distinguished name (DN) parameter" vulnerabilities. */
module LdapInjectionDnFlow = TaintTracking::Global<LdapInjectionDnConfig>;
/**
* DEPRECATED: Use `LdapInjectionFilterFlow` module instead.
*
* A taint-tracking configuration for detecting LDAP injection vulnerabilities
* via the filter parameter of an LDAP search.
*/
class FilterConfiguration extends TaintTracking::Configuration {
deprecated class FilterConfiguration extends TaintTracking::Configuration {
FilterConfiguration() { this = "LdapFilterInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -48,3 +63,19 @@ class FilterConfiguration extends TaintTracking::Configuration {
guard instanceof FilterSanitizerGuard
}
}
private module LdapInjectionFilterConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof FilterSink }
predicate isBarrier(DataFlow::Node node) { node instanceof FilterSanitizer }
}
/** Global taint-tracking for detecting "LDAP injection via the filter parameter" vulnerabilities. */
module LdapInjectionFilterFlow = TaintTracking::Global<LdapInjectionFilterConfig>;
/** Global taint-tracking for detecting "LDAP injection" vulnerabilities. */
module LdapInjectionFlow =
DataFlow::MergePathGraph<LdapInjectionDnFlow::PathNode, LdapInjectionFilterFlow::PathNode,
LdapInjectionDnFlow::PathGraph, LdapInjectionFilterFlow::PathGraph>;

View File

@@ -1,5 +1,5 @@
/**
* Provides a taint-tracking configuration for tracking untrusted user input used in log entries.
* Provides a taint-tracking configuration for tracking "log injection" vulnerabilities.
*
* Note, for performance reasons: only import this file if
* `LogInjection::Configuration` is needed, otherwise
@@ -12,9 +12,11 @@ import semmle.python.dataflow.new.TaintTracking
import LogInjectionCustomizations::LogInjection
/**
* DEPRECATED: Use `LogInjectionFlow` module instead.
*
* A taint-tracking configuration for tracking untrusted user input used in log entries.
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "LogInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -27,3 +29,14 @@ class Configuration extends TaintTracking::Configuration {
guard instanceof SanitizerGuard
}
}
private module LogInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "log injection" vulnerabilities. */
module LogInjectionFlow = TaintTracking::Global<LogInjectionConfig>;

View File

@@ -12,9 +12,11 @@ import semmle.python.dataflow.new.TaintTracking
import PamAuthorizationCustomizations::PamAuthorizationCustomizations
/**
* DEPRECATED: Use `PamAuthorizationFlow` module instead.
*
* A taint-tracking configuration for detecting "PAM Authorization" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "PamAuthorization" }
override predicate isSource(DataFlow::Node node) { node instanceof Source }
@@ -37,3 +39,28 @@ class Configuration extends TaintTracking::Configuration {
exists(VulnPamAuthCall c | c.getArg(0) = node1 | node2 = c)
}
}
private module PamAuthorizationConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
// Models flow from a remotely supplied username field to a PAM `handle`.
// `retval = pam_start(service, username, byref(conv), byref(handle))`
exists(API::CallNode pamStart, DataFlow::Node handle, API::CallNode pointer |
pointer = API::moduleImport("ctypes").getMember(["pointer", "byref"]).getACall() and
pamStart = libPam().getMember("pam_start").getACall() and
pointer = pamStart.getArg(3) and
handle = pointer.getArg(0) and
pamStart.getArg(1) = node1 and
handle = node2
)
or
// Flow from handle to the authenticate call in the final step
exists(VulnPamAuthCall c | c.getArg(0) = node1 | node2 = c)
}
}
/** Global taint-tracking for detecting "PAM Authorization" vulnerabilities. */
module PamAuthorizationFlow = TaintTracking::Global<PamAuthorizationConfig>;

View File

@@ -13,6 +13,8 @@ import semmle.python.dataflow.new.TaintTracking
import PathInjectionCustomizations::PathInjection
/**
* DEPRECATED: Use `PathInjectionFlow` module instead.
*
* A taint-tracking configuration for detecting "path injection" vulnerabilities.
*
* This configuration uses two flow states, `NotNormalized` and `NormalizedUnchecked`,
@@ -25,7 +27,7 @@ import PathInjectionCustomizations::PathInjection
*
* Such checks are ineffective in the `NotNormalized` state.
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "PathInjection" }
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
@@ -74,3 +76,52 @@ class NotNormalized extends DataFlow::FlowState {
class NormalizedUnchecked extends DataFlow::FlowState {
NormalizedUnchecked() { this = "NormalizedUnchecked" }
}
/**
* This configuration uses two flow states, `NotNormalized` and `NormalizedUnchecked`,
* to track the requirement that a file path must be first normalized and then checked
* before it is safe to use.
*
* At sources, paths are assumed not normalized. At normalization points, they change
* state to `NormalizedUnchecked` after which they can be made safe by an appropriate
* check of the prefix.
*
* Such checks are ineffective in the `NotNormalized` state.
*/
module PathInjectionConfig implements DataFlow::StateConfigSig {
class FlowState = DataFlow::FlowState;
predicate isSource(DataFlow::Node source, FlowState state) {
source instanceof Source and state instanceof NotNormalized
}
predicate isSink(DataFlow::Node sink, FlowState state) {
sink instanceof Sink and
(
state instanceof NotNormalized or
state instanceof NormalizedUnchecked
)
}
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate isBarrier(DataFlow::Node node, FlowState state) {
// Block `NotNormalized` paths here, since they change state to `NormalizedUnchecked`
node instanceof Path::PathNormalization and
state instanceof NotNormalized
or
node instanceof Path::SafeAccessCheck and
state instanceof NormalizedUnchecked
}
predicate isAdditionalFlowStep(
DataFlow::Node nodeFrom, FlowState stateFrom, DataFlow::Node nodeTo, FlowState stateTo
) {
nodeFrom = nodeTo.(Path::PathNormalization).getPathArg() and
stateFrom instanceof NotNormalized and
stateTo instanceof NormalizedUnchecked
}
}
/** Global taint-tracking for detecting "path injection" vulnerabilities. */
module PathInjectionFlow = TaintTracking::GlobalWithState<PathInjectionConfig>;

View File

@@ -6,7 +6,6 @@
private import python
private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.DataFlow2
private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts
private import semmle.python.dataflow.new.RemoteFlowSources

View File

@@ -12,9 +12,11 @@ import semmle.python.dataflow.new.TaintTracking
import PolynomialReDoSCustomizations::PolynomialReDoS
/**
* DEPRECATED: Use `PolynomialReDoSFlow` module instead.
*
* A taint-tracking configuration for detecting "polynomial regular expression denial of service (ReDoS)" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "PolynomialReDoS" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -27,3 +29,14 @@ class Configuration extends TaintTracking::Configuration {
guard instanceof SanitizerGuard
}
}
private module PolynomialReDoSConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "polynomial regular expression denial of service (ReDoS)" vulnerabilities. */
module PolynomialReDoSFlow = TaintTracking::Global<PolynomialReDoSConfig>;

View File

@@ -12,9 +12,11 @@ import semmle.python.dataflow.new.TaintTracking
import ReflectedXSSCustomizations::ReflectedXss
/**
* DEPRECATED: Use `ReflectedXssFlow` module instead.
*
* A taint-tracking configuration for detecting "reflected server-side cross-site scripting" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "ReflectedXSS" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -27,3 +29,14 @@ class Configuration extends TaintTracking::Configuration {
guard instanceof SanitizerGuard
}
}
private module ReflectedXssConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "reflected server-side cross-site scripting" vulnerabilities. */
module ReflectedXssFlow = TaintTracking::Global<ReflectedXssConfig>;

View File

@@ -1,5 +1,5 @@
/**
* Provides a taint-tracking configuration for detecting regular expression injection
* Provides a taint-tracking configuration for detecting "regular expression injection"
* vulnerabilities.
*
* Note, for performance reasons: only import this file if
@@ -13,9 +13,11 @@ import semmle.python.dataflow.new.TaintTracking
import RegexInjectionCustomizations::RegexInjection
/**
* DEPRECATED: Use `RegexInjectionFlow` module instead.
*
* A taint-tracking configuration for detecting "reflected server-side cross-site scripting" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "RegexInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -28,3 +30,14 @@ class Configuration extends TaintTracking::Configuration {
guard instanceof SanitizerGuard
}
}
private module RegexInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "regular expression injection" vulnerabilities. */
module RegexInjectionFlow = TaintTracking::Global<RegexInjectionConfig>;

View File

@@ -13,6 +13,8 @@ import semmle.python.Concepts
import ServerSideRequestForgeryCustomizations::ServerSideRequestForgery
/**
* DEPRECATED: Use `FullServerSideRequestForgeryFlow` module instead.
*
* A taint-tracking configuration for detecting "Server-side request forgery" vulnerabilities.
*
* This configuration has a sanitizer to limit results to cases where attacker has full control of URL.
@@ -21,7 +23,7 @@ import ServerSideRequestForgeryCustomizations::ServerSideRequestForgery
* You should use the `fullyControlledRequest` to only select results where all
* URL parts are fully controlled.
*/
class FullServerSideRequestForgeryConfiguration extends TaintTracking::Configuration {
deprecated class FullServerSideRequestForgeryConfiguration extends TaintTracking::Configuration {
FullServerSideRequestForgeryConfiguration() { this = "FullServerSideRequestForgery" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -39,24 +41,51 @@ class FullServerSideRequestForgeryConfiguration extends TaintTracking::Configura
}
}
/**
* This configuration has a sanitizer to limit results to cases where attacker has full control of URL.
* See `PartialServerSideRequestForgery` for a variant without this requirement.
*
* You should use the `fullyControlledRequest` to only select results where all
* URL parts are fully controlled.
*/
private module FullServerSideRequestForgeryConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) {
node instanceof Sanitizer
or
node instanceof FullUrlControlSanitizer
}
}
/**
* Global taint-tracking for detecting "Full server-side request forgery" vulnerabilities.
*
* You should use the `fullyControlledRequest` to only select results where all
* URL parts are fully controlled.
*/
module FullServerSideRequestForgeryFlow = TaintTracking::Global<FullServerSideRequestForgeryConfig>;
/**
* Holds if all URL parts of `request` is fully user controlled.
*/
predicate fullyControlledRequest(Http::Client::Request request) {
exists(FullServerSideRequestForgeryConfiguration fullConfig |
forall(DataFlow::Node urlPart | urlPart = request.getAUrlPart() |
fullConfig.hasFlow(_, urlPart)
)
forall(DataFlow::Node urlPart | urlPart = request.getAUrlPart() |
FullServerSideRequestForgeryFlow::flow(_, urlPart)
)
}
/**
* DEPRECATED: Use `FullServerSideRequestForgeryFlow` module instead.
*
* A taint-tracking configuration for detecting "Server-side request forgery" vulnerabilities.
*
* This configuration has results, even when the attacker does not have full control over the URL.
* See `FullServerSideRequestForgeryConfiguration`, and the `fullyControlledRequest` predicate.
*/
class PartialServerSideRequestForgeryConfiguration extends TaintTracking::Configuration {
deprecated class PartialServerSideRequestForgeryConfiguration extends TaintTracking::Configuration {
PartialServerSideRequestForgeryConfiguration() { this = "PartialServerSideRequestForgery" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -69,3 +98,21 @@ class PartialServerSideRequestForgeryConfiguration extends TaintTracking::Config
guard instanceof SanitizerGuard
}
}
/**
* This configuration has results, even when the attacker does not have full control over the URL.
* See `FullServerSideRequestForgeryConfiguration`, and the `fullyControlledRequest` predicate.
*/
private module PartialServerSideRequestForgeryConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/**
* Global taint-tracking for detecting "partial server-side request forgery" vulnerabilities.
*/
module PartialServerSideRequestForgeryFlow =
TaintTracking::Global<PartialServerSideRequestForgeryConfig>;

View File

@@ -12,9 +12,11 @@ import semmle.python.dataflow.new.TaintTracking
import SqlInjectionCustomizations::SqlInjection
/**
* DEPRECATED: Use `SqlInjectionFlow` module instead.
*
* A taint-tracking configuration for detecting "SQL injection" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "SqlInjection" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -27,3 +29,14 @@ class Configuration extends TaintTracking::Configuration {
guard instanceof SanitizerGuard
}
}
private module SqlInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "SQL injection" vulnerabilities. */
module SqlInjectionFlow = TaintTracking::Global<SqlInjectionConfig>;

View File

@@ -12,9 +12,11 @@ import semmle.python.dataflow.new.TaintTracking
import StackTraceExposureCustomizations::StackTraceExposure
/**
* DEPRECATED: Use `StackTraceExposureFlow` module instead.
*
* A taint-tracking configuration for detecting "stack trace exposure" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "StackTraceExposure" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -36,3 +38,23 @@ class Configuration extends TaintTracking::Configuration {
)
}
}
private module StackTraceExposureConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
// A stack trace is accessible as the `__traceback__` attribute of a caught exception.
// see https://docs.python.org/3/reference/datamodel.html#traceback-objects
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(DataFlow::AttrRead attr | attr.getAttributeName() = "__traceback__" |
nodeFrom = attr.getObject() and
nodeTo = attr
)
}
}
/** Global taint-tracking for detecting "stack trace exposure" vulnerabilities. */
module StackTraceExposureFlow = TaintTracking::Global<StackTraceExposureConfig>;

View File

@@ -1,5 +1,5 @@
/**
* Provides a taint-tracking configuration for detecting "command injection" vulnerabilities.
* Provides a taint-tracking configuration for detecting "tar slip" vulnerabilities.
*
* Note, for performance reasons: only import this file if
* `TarSlip::Configuration` is needed, otherwise
@@ -12,9 +12,11 @@ import semmle.python.dataflow.new.TaintTracking
import TarSlipCustomizations::TarSlip
/**
* A taint-tracking configuration for detecting "command injection" vulnerabilities.
* DEPRECATED: Use `TarSlipFlow` module instead.
*
* A taint-tracking configuration for detecting "tar slip" vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
deprecated class Configuration extends TaintTracking::Configuration {
Configuration() { this = "TarSlip" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
@@ -23,3 +25,14 @@ class Configuration extends TaintTracking::Configuration {
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
}
private module TarSlipConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof Source }
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
/** Global taint-tracking for detecting "tar slip" vulnerabilities. */
module TarSlipFlow = TaintTracking::Global<TarSlipConfig>;

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