Merge branch 'main' into redsun82/swift-file-label-caching

This commit is contained in:
Paolo Tranquilli
2022-07-26 15:20:27 +02:00
247 changed files with 3210 additions and 1763 deletions

View File

@@ -1,3 +1,9 @@
## 0.3.1
### Minor Analysis Improvements
* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the C++ logical "and", and variable declarations in conditions.
## 0.3.0
### Deprecated APIs

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* Under certain circumstances a variable declaration that is not also a definition could be associated with a `Variable` that did not have the definition as a `VariableDeclarationEntry`. This is now fixed, and a unique `Variable` will exist that has both the declaration and the definition as a `VariableDeclarationEntry`.

View File

@@ -1,4 +1,5 @@
---
category: minorAnalysis
---
* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the the C++ logical and variable declarations in conditions.
## 0.3.1
### Minor Analysis Improvements
* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the C++ logical "and", and variable declarations in conditions.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.3.0
lastReleaseVersion: 0.3.1

View File

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

View File

@@ -6,6 +6,7 @@
import semmle.code.cpp.Location
private import semmle.code.cpp.Enclosing
private import semmle.code.cpp.internal.ResolveClass
private import semmle.code.cpp.internal.ResolveGlobalVariable
/**
* Get the `Element` that represents this `@element`.
@@ -28,9 +29,12 @@ Element mkElement(@element e) { unresolveElement(result) = e }
pragma[inline]
@element unresolveElement(Element e) {
not result instanceof @usertype and
not result instanceof @variable and
result = e
or
e = resolveClass(result)
or
e = resolveGlobalVariable(result)
}
/**

View File

@@ -6,6 +6,7 @@ import semmle.code.cpp.Element
import semmle.code.cpp.exprs.Access
import semmle.code.cpp.Initializer
private import semmle.code.cpp.internal.ResolveClass
private import semmle.code.cpp.internal.ResolveGlobalVariable
/**
* A C/C++ variable. For example, in the following code there are four
@@ -32,6 +33,8 @@ private import semmle.code.cpp.internal.ResolveClass
* can have multiple declarations.
*/
class Variable extends Declaration, @variable {
Variable() { isVariable(underlyingElement(this)) }
override string getAPrimaryQlClass() { result = "Variable" }
/** Gets the initializer of this variable, if any. */

View File

@@ -0,0 +1,57 @@
private predicate hasDefinition(@globalvariable g) {
exists(@var_decl vd | var_decls(vd, g, _, _, _) | var_def(vd))
}
private predicate onlyOneCompleteGlobalVariableExistsWithMangledName(@mangledname name) {
strictcount(@globalvariable g | hasDefinition(g) and mangled_name(g, name)) = 1
}
/** Holds if `g` is a unique global variable with a definition named `name`. */
private predicate isGlobalWithMangledNameAndWithDefinition(@mangledname name, @globalvariable g) {
hasDefinition(g) and
mangled_name(g, name) and
onlyOneCompleteGlobalVariableExistsWithMangledName(name)
}
/** Holds if `g` is a global variable without a definition named `name`. */
private predicate isGlobalWithMangledNameAndWithoutDefinition(@mangledname name, @globalvariable g) {
not hasDefinition(g) and
mangled_name(g, name)
}
/**
* Holds if `incomplete` is a global variable without a definition, and there exists
* a unique global variable `complete` with the same name that does have a definition.
*/
private predicate hasTwinWithDefinition(@globalvariable incomplete, @globalvariable complete) {
exists(@mangledname name |
not variable_instantiation(incomplete, complete) and
isGlobalWithMangledNameAndWithoutDefinition(name, incomplete) and
isGlobalWithMangledNameAndWithDefinition(name, complete)
)
}
import Cached
cached
private module Cached {
/**
* If `v` is a global variable without a definition, and there exists a unique
* global variable with the same name that does have a definition, then the
* result is that unique global variable. Otherwise, the result is `v`.
*/
cached
@variable resolveGlobalVariable(@variable v) {
hasTwinWithDefinition(v, result)
or
not hasTwinWithDefinition(v, _) and
result = v
}
cached
predicate isVariable(@variable v) {
not v instanceof @globalvariable
or
v = resolveGlobalVariable(_)
}
}

View File

@@ -1,3 +1,9 @@
## 0.3.0
### Breaking Changes
* Contextual queries and the query libraries they depend on have been moved to the `codeql/cpp-all` package.
## 0.2.0
## 0.1.4

View File

@@ -74,13 +74,12 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
from
MustFlowPathNode source, MustFlowPathNode sink, VariableAddressInstruction var,
ReturnStackAllocatedMemoryConfig conf, Function f
ReturnStackAllocatedMemoryConfig conf
where
conf.hasFlowPath(source, sink) and
conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
source.getNode().asInstruction() = var and
// Only raise an alert if we're returning from the _same_ callable as the on that
// declared the stack variable.
var.getEnclosingFunction() = pragma[only_bind_into](f) and
sink.getNode().getEnclosingCallable() = pragma[only_bind_into](f)
var.getEnclosingFunction() = sink.getNode().getEnclosingCallable()
select sink.getNode(), source, sink, "May return stack-allocated memory from $@.", var.getAst(),
var.getAst().toString()

View File

@@ -77,7 +77,7 @@ class ExecState extends DataFlow::FlowState {
ExecState() {
this =
"ExecState (" + fst.getLocation() + " | " + fst + ", " + snd.getLocation() + " | " + snd + ")" and
interestingConcatenation(fst, snd)
interestingConcatenation(pragma[only_bind_into](fst), pragma[only_bind_into](snd))
}
DataFlow::Node getFstNode() { result = fst }

View File

@@ -1,4 +1,5 @@
---
category: breaking
---
## 0.3.0
### Breaking Changes
* Contextual queries and the query libraries they depend on have been moved to the `codeql/cpp-all` package.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.2.0
lastReleaseVersion: 0.3.0

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries
version: 0.2.1-dev
version: 0.3.1-dev
groups:
- cpp
- queries

View File

@@ -4,11 +4,7 @@
| c.c:6:5:6:6 | ls | array of 4 {int} | 1 |
| c.c:8:5:8:7 | iss | array of 4 {array of 2 {int}} | 1 |
| c.c:12:11:12:11 | i | typedef {int} as "int_alias" | 1 |
| c.h:4:12:4:13 | ks | array of {int} | 1 |
| c.h:8:12:8:14 | iss | array of {array of 2 {int}} | 1 |
| c.h:10:12:10:12 | i | int | 1 |
| d.cpp:3:7:3:8 | xs | array of {int} | 1 |
| d.h:3:14:3:15 | xs | array of 2 {int} | 1 |
| file://:0:0:0:0 | (unnamed parameter 0) | reference to {const {struct __va_list_tag}} | 1 |
| file://:0:0:0:0 | (unnamed parameter 0) | rvalue reference to {struct __va_list_tag} | 1 |
| file://:0:0:0:0 | fp_offset | unsigned int | 1 |

View File

@@ -1,3 +1,5 @@
## 1.2.1
## 1.2.0
## 1.1.4

View File

@@ -0,0 +1 @@
## 1.2.1

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.2.0
lastReleaseVersion: 1.2.1

View File

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

View File

@@ -1,3 +1,5 @@
## 1.2.1
## 1.2.0
## 1.1.4

View File

@@ -0,0 +1 @@
## 1.2.1

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 1.2.0
lastReleaseVersion: 1.2.1

View File

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

View File

@@ -1,3 +1,5 @@
## 0.3.1
## 0.3.0
### Deprecated APIs

View File

@@ -0,0 +1 @@
## 0.3.1

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.3.0
lastReleaseVersion: 0.3.1

View File

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

View File

@@ -1,3 +1,9 @@
## 0.3.0
### Breaking Changes
* Contextual queries and the query libraries they depend on have been moved to the `codeql/csharp-all` package.
## 0.2.0
### Query Metadata Changes

View File

@@ -1,4 +1,5 @@
---
category: breaking
---
## 0.3.0
### Breaking Changes
* Contextual queries and the query libraries they depend on have been moved to the `codeql/csharp-all` package.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.2.0
lastReleaseVersion: 0.3.0

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries
version: 0.2.1-dev
version: 0.3.1-dev
groups:
- csharp
- queries

View File

@@ -2,7 +2,54 @@ function RegisterExtractorPack(id)
local extractor = GetPlatformToolsDirectory() ..
'Semmle.Extraction.CSharp.Driver'
if OperatingSystem == 'windows' then extractor = extractor .. '.exe' end
function DotnetMatcherBuild(compilerName, compilerPath, compilerArguments,
_languageId)
if compilerName ~= 'dotnet' and compilerName ~= 'dotnet.exe' then
return nil
end
-- The dotnet CLI has the following usage instructions:
-- dotnet [sdk-options] [command] [command-options] [arguments]
-- we are interested in dotnet build, which has the following usage instructions:
-- dotnet [options] build [<PROJECT | SOLUTION>...]
-- For now, parse the command line as follows:
-- Everything that starts with `-` (or `/`) will be ignored.
-- The first non-option argument is treated as the command.
-- if that's `build`, we append `/p:UseSharedCompilation=false` to the command line,
-- otherwise we do nothing.
local match = false
local argv = compilerArguments.argv
if OperatingSystem == 'windows' then
-- let's hope that this split matches the escaping rules `dotnet` applies to command line arguments
-- or, at least, that it is close enough
argv =
NativeArgumentsToArgv(compilerArguments.nativeArgumentPointer)
end
for i, arg in ipairs(argv) do
-- dotnet options start with either - or / (both are legal)
local firstCharacter = string.sub(arg, 1, 1)
if not (firstCharacter == '-') and not (firstCharacter == '/') then
Log(1, 'Dotnet subcommand detected: %s', arg)
if arg == 'build' then match = true end
break
end
end
if match then
return {
order = ORDER_REPLACE,
invocation = BuildExtractorInvocation(id, compilerPath,
compilerPath,
compilerArguments, nil, {
'/p:UseSharedCompilation=false'
})
}
end
return nil
end
local windowsMatchers = {
DotnetMatcherBuild,
CreatePatternMatcher({'^dotnet%.exe$'}, MatchCompilerName, extractor, {
prepend = {'--dotnetexec', '--cil'},
order = ORDER_BEFORE
@@ -10,22 +57,21 @@ function RegisterExtractorPack(id)
CreatePatternMatcher({'^csc.*%.exe$'}, MatchCompilerName, extractor, {
prepend = {'--compiler', '"${compiler}"', '--cil'},
order = ORDER_BEFORE
}),
CreatePatternMatcher({'^fakes.*%.exe$', 'moles.*%.exe'},
MatchCompilerName, nil, {trace = false})
}
local posixMatchers = {
CreatePatternMatcher({'^mcs%.exe$', '^csc%.exe$'}, MatchCompilerName,
extractor, {
prepend = {'--compiler', '"${compiler}"', '--cil'},
order = ORDER_BEFORE
}),
DotnetMatcherBuild,
CreatePatternMatcher({'^mono', '^dotnet$'}, MatchCompilerName,
extractor, {
prepend = {'--dotnetexec', '--cil'},
order = ORDER_BEFORE
}),
CreatePatternMatcher({'^mcs%.exe$', '^csc%.exe$'}, MatchCompilerName,
extractor, {
prepend = {'--compiler', '"${compiler}"', '--cil'},
order = ORDER_BEFORE
}), function(compilerName, compilerPath, compilerArguments, _languageId)
if MatchCompilerName('^msbuild$', compilerName, compilerPath,
compilerArguments) or
@@ -49,7 +95,6 @@ function RegisterExtractorPack(id)
else
return posixMatchers
end
end
-- Return a list of minimum supported versions of the configuration file format

View File

@@ -11,14 +11,17 @@ CodeQL.
Languages and compilers
#######################
CodeQL supports the following languages and compilers.
The current versions of the CodeQL CLI (`changelog <https://github.com/github/codeql-cli-binaries/blob/main/CHANGELOG.md>`__, `releases <https://github.com/github/codeql-cli-binaries/releases>`__),
CodeQL library packs (`source <https://github.com/github/codeql/tree/codeql-cli/latest>`__),
and CodeQL bundle (`releases <https://github.com/github/codeql-action/releases>`__)
support the following languages and compilers.
.. include:: ../support/reusables/versions-compilers.rst
Frameworks and libraries
########################
The libraries and queries in the current version of CodeQL have been explicitly checked against the libraries and frameworks listed below.
The current versions of the CodeQL library and query packs (`source <https://github.com/github/codeql/tree/codeql-cli/latest>`__) have been explicitly checked against the libraries and frameworks listed below.
.. pull-quote::

View File

@@ -1,6 +1,10 @@
C and C++ built-in support
================================
Provided by the current versions of the
CodeQL query pack ``codeql/cpp-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/cpp/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/cpp/ql/src>`__)
and the CodeQL library pack ``codeql/cpp-all`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/cpp/ql/lib/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/cpp/ql/lib>`__).
.. csv-table::
:header-rows: 1
:class: fullWidthTable
@@ -14,6 +18,10 @@ C and C++ built-in support
C# built-in support
================================
Provided by the current versions of the
CodeQL query pack ``codeql/csharp-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/csharp/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/csharp/ql/src>`__)
and the CodeQL library pack ``codeql/csharp-all`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/csharp/ql/lib/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/csharp/ql/lib>`__).
.. csv-table::
:header-rows: 1
:class: fullWidthTable
@@ -33,6 +41,10 @@ C# built-in support
Go built-in support
================================
Provided by the current versions of the
CodeQL query pack ``codeql/go-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/go/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/go/ql/src>`__)
and the CodeQL library pack ``codeql/go-all`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/go/ql/lib/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/go/ql/lib>`__).
.. csv-table::
:header-rows: 1
:class: fullWidthTable
@@ -84,6 +96,10 @@ Go built-in support
Java built-in support
==================================
Provided by the current versions of the
CodeQL query pack ``codeql/java-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/java/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/java/ql/src>`__)
and the CodeQL library pack ``codeql/java-all`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/java/ql/lib/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/java/ql/lib>`__).
.. csv-table::
:header-rows: 1
:class: fullWidthTable
@@ -113,6 +129,10 @@ Java built-in support
JavaScript and TypeScript built-in support
=======================================================
Provided by the current versions of the
CodeQL query pack ``codeql/javascript-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/javascript/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/javascript/ql/src>`__)
and the CodeQL library pack ``codeql/javascript-all`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/javascript/ql/lib/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/javascript/ql/lib>`__).
.. csv-table::
:header-rows: 1
:class: fullWidthTable
@@ -156,6 +176,10 @@ JavaScript and TypeScript built-in support
Python built-in support
====================================
Provided by the current versions of the
CodeQL query pack ``codeql/python-queries`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/python/ql/src/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/python/ql/src>`__)
and the CodeQL library pack ``codeql/python-all`` (`changelog <https://github.com/github/codeql/tree/codeql-cli/latest/python/ql/lib/CHANGELOG.md>`__, `source <https://github.com/github/codeql/tree/codeql-cli/latest/python/ql/lib>`__).
.. csv-table::
:header-rows: 1
:class: fullWidthTable

View File

@@ -1,3 +1,5 @@
## 0.2.1
## 0.2.0
### Deprecated APIs

View File

@@ -0,0 +1 @@
## 0.2.1

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.2.0
lastReleaseVersion: 0.2.1

View File

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

View File

@@ -1,3 +1,5 @@
## 0.2.1
## 0.2.0
## 0.1.4

View File

@@ -0,0 +1 @@
## 0.2.1

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.2.0
lastReleaseVersion: 0.2.1

View File

@@ -1,5 +1,5 @@
name: codeql/go-queries
version: 0.2.1-dev
version: 0.2.2-dev
groups:
- go
- queries

View File

@@ -2124,7 +2124,13 @@ open class KotlinFileExtractor(
}
isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "iterator") && c.origin == IrStatementOrigin.FOR_LOOP_ITERATOR -> {
findTopLevelFunctionOrWarn("kotlin.jvm.internal.iterator", "kotlin.jvm.internal.ArrayIteratorKt", c)?.let { iteratorFn ->
extractRawMethodAccess(iteratorFn, c, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, listOf((c.dispatchReceiver!!.type as IrSimpleType).arguments.first().typeOrNull!!))
val typeArgs = (c.dispatchReceiver!!.type as IrSimpleType).arguments.map {
when(it) {
is IrTypeProjection -> it.type
else -> pluginContext.irBuiltIns.anyNType
}
}
extractRawMethodAccess(iteratorFn, c, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, typeArgs)
}
}
isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "get") && c.origin == IrStatementOrigin.GET_ARRAY_ELEMENT -> {

View File

@@ -51,6 +51,8 @@ private val specialFunctions = mapOf(
makeDescription(FqName("java.lang.Number"), "toFloat") to "floatValue",
makeDescription(StandardNames.FqNames.number.toSafe(), "toDouble") to "doubleValue",
makeDescription(FqName("java.lang.Number"), "toDouble") to "doubleValue",
makeDescription(StandardNames.FqNames.string.toSafe(), "get") to "charAt",
makeDescription(FqName("java.lang.String"), "get") to "charAt",
)
private val specialFunctionShortNames = specialFunctions.keys.map { it.functionName }.toSet()

View File

@@ -1,3 +1,16 @@
## 0.3.1
### New Features
* Added an `ErrorType` class. An instance of this class will be used if an extractor is unable to extract a type, or if an up/downgrade script is unable to provide a type.
### Minor Analysis Improvements
* Added data-flow models for `java.util.Properites`. Additional results may be found where relevant data is stored in and then retrieved from a `Properties` instance.
* Added `Modifier.isInline()`.
* Removed Kotlin-specific database and QL structures for loops and `break`/`continue` statements. The Kotlin extractor was changed to reuse the Java structures for these constructs.
* Added additional flow sources for uses of external storage on Android.
## 0.3.0
### Deprecated APIs

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
Added additional flow sources for uses of external storage on Android.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Removed Kotlin-specific database and QL structures for loops and `break`/`continue` statements. The Kotlin extractor was changed to reuse the Java structures for these constructs.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
Added `Modifier.isInline()`.

View File

@@ -1,4 +0,0 @@
---
category: feature
---
* Added an `ErrorType` class. An instance of this class will be used if an extractor is unable to extract a type, or if an up/downgrade script is unable to provide a type.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added data-flow models for `java.util.Properites`. Additional results may be found where relevant data is stored in and then retrieved from a `Properties` instance.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added data flow models for `java.util.Scanner`.

View File

@@ -0,0 +1,12 @@
## 0.3.1
### New Features
* Added an `ErrorType` class. An instance of this class will be used if an extractor is unable to extract a type, or if an up/downgrade script is unable to provide a type.
### Minor Analysis Improvements
* Added data-flow models for `java.util.Properites`. Additional results may be found where relevant data is stored in and then retrieved from a `Properties` instance.
* Added `Modifier.isInline()`.
* Removed Kotlin-specific database and QL structures for loops and `break`/`continue` statements. The Kotlin extractor was changed to reuse the Java structures for these constructs.
* Added additional flow sources for uses of external storage on Android.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.3.0
lastReleaseVersion: 0.3.1

View File

@@ -1,5 +1,5 @@
name: codeql/java-all
version: 0.3.1-dev
version: 0.3.2-dev
groups: java
dbscheme: config/semmlecode.dbscheme
extractor: java

View File

@@ -244,8 +244,26 @@ private class ContainerFlowSummaries extends SummaryModelCsv {
"java.util;Properties;true;getProperty;(String);;Argument[-1].MapValue;ReturnValue;value;manual",
"java.util;Properties;true;getProperty;(String,String);;Argument[-1].MapValue;ReturnValue;value;manual",
"java.util;Properties;true;getProperty;(String,String);;Argument[1];ReturnValue;value;manual",
"java.util;Scanner;true;next;(Pattern);;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;next;(String);;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual",
"java.util;Scanner;true;findInLine;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;nextBigDecimal;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;nextBigInteger;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;nextBoolean;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;nextByte;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;nextDouble;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;nextFloat;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;nextInt;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;nextLine;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;nextLong;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;nextShort;;;Argument[-1];ReturnValue;taint;manual",
"java.util;Scanner;true;reset;;;Argument[-1];ReturnValue;value;manual",
"java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual",
"java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual",
"java.util;Scanner;true;useLocale;;;Argument[-1];ReturnValue;value;manual",
"java.util;Scanner;true;useRadix;;;Argument[-1];ReturnValue;value;manual",
"java.util;SortedMap;true;headMap;(Object);;Argument[-1].MapKey;ReturnValue.MapKey;value;manual",
"java.util;SortedMap;true;headMap;(Object);;Argument[-1].MapValue;ReturnValue.MapValue;value;manual",
"java.util;SortedMap;true;subMap;(Object,Object);;Argument[-1].MapKey;ReturnValue.MapKey;value;manual",

View File

@@ -1,3 +1,15 @@
## 0.3.0
### Breaking Changes
* Contextual queries and the query libraries they depend on have been moved to the `codeql/java-all` package.
### New Queries
* A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added.
This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered
to receive system intents.
## 0.2.0
### Minor Analysis Improvements

View File

@@ -1,6 +0,0 @@
---
category: newQuery
---
* A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added.
This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered
to receive system intents.

View File

@@ -1,4 +0,0 @@
---
category: breaking
---
* Contextual queries and the query libraries they depend on have been moved to the `codeql/java-all` package.

View File

@@ -0,0 +1,11 @@
## 0.3.0
### Breaking Changes
* Contextual queries and the query libraries they depend on have been moved to the `codeql/java-all` package.
### New Queries
* A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added.
This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered
to receive system intents.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.2.0
lastReleaseVersion: 0.3.0

View File

@@ -1,5 +1,5 @@
name: codeql/java-queries
version: 0.2.1-dev
version: 0.3.1-dev
groups:
- java
- queries

View File

@@ -0,0 +1,9 @@
| test.kt:5:14:5:14 | hasNext(...) |
| test.kt:5:14:5:14 | iterator(...) |
| test.kt:5:14:5:14 | next(...) |
| test.kt:6:14:6:14 | hasNext(...) |
| test.kt:6:14:6:14 | iterator(...) |
| test.kt:6:14:6:14 | next(...) |
| test.kt:7:14:7:14 | hasNext(...) |
| test.kt:7:14:7:14 | iterator(...) |
| test.kt:7:14:7:14 | next(...) |

View File

@@ -0,0 +1,11 @@
fun test(x: Array<String>, y: Array<*>, z: IntArray): Int {
var ret = 0
for (el in x) { ret += 1 }
for (el in y) { ret += 1 }
for (el in z) { ret += 1 }
return ret
}

View File

@@ -0,0 +1,4 @@
import java
from MethodAccess ma
select ma

View File

@@ -8,7 +8,7 @@ reflection.kt:
# 46| 0: [TypeAccess] String
# 47| 5: [BlockStmt] { ... }
# 47| 0: [ReturnStmt] return ...
# 47| 0: [MethodAccess] get(...)
# 47| 0: [MethodAccess] charAt(...)
# 47| -1: [ExtensionReceiverAccess] this
# 47| 0: [SubExpr] ... - ...
# 47| 0: [MethodAccess] length(...)

View File

@@ -0,0 +1,5 @@
public class Test {
public char f(String s) { return s.charAt(0); }
}

View File

@@ -0,0 +1,2 @@
| Test.java:3:36:3:46 | charAt(...) |
| test.kt:2:20:2:23 | charAt(...) |

View File

@@ -0,0 +1,2 @@
fun f(x: String) = x[0]

View File

@@ -0,0 +1,4 @@
import java
from MethodAccess ma
select ma

View File

@@ -0,0 +1,328 @@
package generatedtest;
import java.io.File;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.util.Scanner;
import java.util.regex.Pattern;
// Test case generated by GenerateFlowTestCase.ql
public class Test {
Object source() {
return null;
}
void sink(Object o) {}
public void test() throws Exception {
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
File in = (File) source();
out = new Scanner(in);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
File in = (File) source();
out = new Scanner(in, (Charset) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
File in = (File) source();
out = new Scanner(in, (String) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
InputStream in = (InputStream) source();
out = new Scanner(in);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
InputStream in = (InputStream) source();
out = new Scanner(in, (Charset) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
InputStream in = (InputStream) source();
out = new Scanner(in, (String) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
Path in = (Path) source();
out = new Scanner(in);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
Path in = (Path) source();
out = new Scanner(in, (Charset) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
Path in = (Path) source();
out = new Scanner(in, (String) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
Readable in = (Readable) source();
out = new Scanner(in);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
ReadableByteChannel in = (ReadableByteChannel) source();
out = new Scanner(in);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
ReadableByteChannel in = (ReadableByteChannel) source();
out = new Scanner(in, (Charset) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
ReadableByteChannel in = (ReadableByteChannel) source();
out = new Scanner(in, (String) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual"
Scanner out = null;
String in = (String) source();
out = new Scanner(in);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;findInLine;;;Argument[-1];ReturnValue;taint;manual"
String out = null;
Scanner in = (Scanner) source();
out = in.findInLine((Pattern) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;findInLine;;;Argument[-1];ReturnValue;taint;manual"
String out = null;
Scanner in = (Scanner) source();
out = in.findInLine((String) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual"
String out = null;
Scanner in = (Scanner) source();
out = in.findWithinHorizon((Pattern) null, 0);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual"
String out = null;
Scanner in = (Scanner) source();
out = in.findWithinHorizon((String) null, 0);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual"
String out = null;
Scanner in = (Scanner) source();
out = in.next((Pattern) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual"
String out = null;
Scanner in = (Scanner) source();
out = in.next((String) null);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual"
String out = null;
Scanner in = (Scanner) source();
out = in.next();
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextBigDecimal;;;Argument[-1];ReturnValue;taint;manual"
BigDecimal out = null;
Scanner in = (Scanner) source();
out = in.nextBigDecimal();
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextBigInteger;;;Argument[-1];ReturnValue;taint;manual"
BigInteger out = null;
Scanner in = (Scanner) source();
out = in.nextBigInteger();
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextBigInteger;;;Argument[-1];ReturnValue;taint;manual"
BigInteger out = null;
Scanner in = (Scanner) source();
out = in.nextBigInteger(0);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextBoolean;;;Argument[-1];ReturnValue;taint;manual"
boolean out = false;
Scanner in = (Scanner) source();
out = in.nextBoolean();
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextByte;;;Argument[-1];ReturnValue;taint;manual"
byte out = 0;
Scanner in = (Scanner) source();
out = in.nextByte();
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextByte;;;Argument[-1];ReturnValue;taint;manual"
byte out = 0;
Scanner in = (Scanner) source();
out = in.nextByte(0);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextDouble;;;Argument[-1];ReturnValue;taint;manual"
double out = 0;
Scanner in = (Scanner) source();
out = in.nextDouble();
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextFloat;;;Argument[-1];ReturnValue;taint;manual"
float out = 0;
Scanner in = (Scanner) source();
out = in.nextFloat();
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextInt;;;Argument[-1];ReturnValue;taint;manual"
int out = 0;
Scanner in = (Scanner) source();
out = in.nextInt();
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextInt;;;Argument[-1];ReturnValue;taint;manual"
int out = 0;
Scanner in = (Scanner) source();
out = in.nextInt(0);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextLine;;;Argument[-1];ReturnValue;taint;manual"
String out = null;
Scanner in = (Scanner) source();
out = in.nextLine();
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextLong;;;Argument[-1];ReturnValue;taint;manual"
long out = 0;
Scanner in = (Scanner) source();
out = in.nextLong();
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextLong;;;Argument[-1];ReturnValue;taint;manual"
long out = 0;
Scanner in = (Scanner) source();
out = in.nextLong(0);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextShort;;;Argument[-1];ReturnValue;taint;manual"
short out = 0;
Scanner in = (Scanner) source();
out = in.nextShort();
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;nextShort;;;Argument[-1];ReturnValue;taint;manual"
short out = 0;
Scanner in = (Scanner) source();
out = in.nextShort(0);
sink(out); // $ hasTaintFlow
}
{
// "java.util;Scanner;true;reset;;;Argument[-1];ReturnValue;value;manual"
Scanner out = null;
Scanner in = (Scanner) source();
out = in.reset();
sink(out); // $ hasValueFlow
}
{
// "java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual"
Scanner out = null;
Scanner in = (Scanner) source();
out = in.skip((Pattern) null);
sink(out); // $ hasValueFlow
}
{
// "java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual"
Scanner out = null;
Scanner in = (Scanner) source();
out = in.skip((String) null);
sink(out); // $ hasValueFlow
}
{
// "java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual"
Scanner out = null;
Scanner in = (Scanner) source();
out = in.useDelimiter((Pattern) null);
sink(out); // $ hasValueFlow
}
{
// "java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual"
Scanner out = null;
Scanner in = (Scanner) source();
out = in.useDelimiter((String) null);
sink(out); // $ hasValueFlow
}
{
// "java.util;Scanner;true;useLocale;;;Argument[-1];ReturnValue;value;manual"
Scanner out = null;
Scanner in = (Scanner) source();
out = in.useLocale(null);
sink(out); // $ hasValueFlow
}
{
// "java.util;Scanner;true;useRadix;;;Argument[-1];ReturnValue;value;manual"
Scanner out = null;
Scanner in = (Scanner) source();
out = in.useRadix(0);
sink(out); // $ hasValueFlow
}
}
}

View File

@@ -0,0 +1,2 @@
import java
import TestUtilities.InlineFlowTest

View File

@@ -1,3 +1,11 @@
## 0.2.1
### Minor Analysis Improvements
* The `chownr` library is now modeled as a sink for the `js/path-injection` query.
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).
* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query.
## 0.2.0
### Major Analysis Improvements

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The `chownr` library is now modeled as a sink for the `js/path-injection` query.

View File

@@ -0,0 +1,7 @@
## 0.2.1
### Minor Analysis Improvements
* The `chownr` library is now modeled as a sink for the `js/path-injection` query.
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).
* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.2.0
lastReleaseVersion: 0.2.1

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-all
version: 0.2.1-dev
version: 0.2.2-dev
groups: javascript
dbscheme: semmlecode.javascript.dbscheme
extractor: javascript

View File

@@ -28,6 +28,9 @@ module Actions {
/** Gets the `jobs` mapping from job IDs to job definitions in this workflow. */
YAMLMapping getJobs() { result = this.lookup("jobs") }
/** Gets the name of the workflow. */
string getName() { result = this.lookup("name").(YAMLString).getValue() }
/** Gets the name of the workflow file. */
string getFileName() { result = this.getFile().getBaseName() }
@@ -129,6 +132,9 @@ module Actions {
/** Gets the value of the `if` field in this step, if any. */
StepIf getIf() { result.getStep() = this }
/** Gets the ID of this step, if any. */
string getId() { result = this.lookup("id").(YAMLString).getValue() }
}
/**

View File

@@ -1,3 +1,9 @@
## 0.3.0
### Breaking Changes
* Contextual queries and the query libraries they depend on have been moved to the `codeql/javascript-all` package.
## 0.2.0
### Minor Analysis Improvements

View File

@@ -1,4 +1,5 @@
---
category: breaking
---
## 0.3.0
### Breaking Changes
* Contextual queries and the query libraries they depend on have been moved to the `codeql/javascript-all` package.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.2.0
lastReleaseVersion: 0.3.0

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-queries
version: 0.2.1-dev
version: 0.3.1-dev
groups:
- javascript
- queries

View File

@@ -1,3 +1,18 @@
## 0.5.1
### Deprecated APIs
- The documentation of API graphs (the `API` module) has been expanded, and some of the members predicates of `API::Node`
have been renamed as follows:
- `getAnImmediateUse` -> `asSource`
- `getARhs` -> `asSink`
- `getAUse` -> `getAValueReachableFromSource`
- `getAValueReachingRhs` -> `getAValueReachingSink`
### Minor Analysis Improvements
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).
## 0.5.0
### Deprecated APIs

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).

View File

@@ -1,6 +1,6 @@
---
category: deprecated
---
## 0.5.1
### Deprecated APIs
- The documentation of API graphs (the `API` module) has been expanded, and some of the members predicates of `API::Node`
have been renamed as follows:
@@ -8,3 +8,7 @@ category: deprecated
- `getARhs` -> `asSink`
- `getAUse` -> `getAValueReachableFromSource`
- `getAValueReachingRhs` -> `getAValueReachingSink`
### Minor Analysis Improvements
* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively).

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.5.0
lastReleaseVersion: 0.5.1

View File

@@ -1,5 +1,5 @@
name: codeql/python-all
version: 0.5.1-dev
version: 0.5.2-dev
groups: python
dbscheme: semmlecode.python.dbscheme
extractor: python

View File

@@ -74,9 +74,7 @@ private module NotExposed {
}
/** DEPRECATED: Alias for fullyQualifiedToApiGraphPath */
deprecated string fullyQualifiedToAPIGraphPath(string fullyQaulified) {
result = fullyQualifiedToApiGraphPath(fullyQaulified)
}
deprecated predicate fullyQualifiedToAPIGraphPath = fullyQualifiedToApiGraphPath/1;
bindingset[this]
abstract class FindSubclassesSpec extends string {

View File

@@ -1,3 +1,9 @@
## 0.3.0
### Breaking Changes
* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package.
## 0.2.0
### Major Analysis Improvements

View File

@@ -2,3 +2,4 @@
category: breaking
---
* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package.

View File

@@ -0,0 +1,5 @@
## 0.3.0
### Breaking Changes
* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.2.0
lastReleaseVersion: 0.3.0

View File

@@ -1,5 +1,5 @@
name: codeql/python-queries
version: 0.2.1-dev
version: 0.3.1-dev
groups:
- python
- queries

View File

@@ -1,161 +1,112 @@
use crate::trap;
use node_types::{EntryKind, Field, NodeTypeMap, Storage, TypeName};
use std::borrow::Cow;
use std::collections::BTreeMap as Map;
use std::collections::BTreeSet as Set;
use std::fmt;
use std::io::Write;
use std::path::Path;
use tracing::{error, info, span, Level};
use tree_sitter::{Language, Node, Parser, Range, Tree};
pub struct TrapWriter {
/// The accumulated trap entries
trap_output: Vec<TrapEntry>,
/// A counter for generating fresh labels
counter: u32,
/// cache of global keys
global_keys: std::collections::HashMap<String, Label>,
pub fn populate_file(writer: &mut trap::Writer, absolute_path: &Path) -> trap::Label {
let (file_label, fresh) =
writer.global_id(&trap::full_id_for_file(&normalize_path(absolute_path)));
if fresh {
writer.add_tuple(
"files",
vec![
trap::Arg::Label(file_label),
trap::Arg::String(normalize_path(absolute_path)),
],
);
populate_parent_folders(writer, file_label, absolute_path.parent());
}
file_label
}
pub fn new_trap_writer() -> TrapWriter {
TrapWriter {
counter: 0,
trap_output: Vec::new(),
global_keys: std::collections::HashMap::new(),
fn populate_empty_file(writer: &mut trap::Writer) -> trap::Label {
let (file_label, fresh) = writer.global_id("empty;sourcefile");
if fresh {
writer.add_tuple(
"files",
vec![
trap::Arg::Label(file_label),
trap::Arg::String("".to_string()),
],
);
}
file_label
}
impl TrapWriter {
/// Gets a label that will hold the unique ID of the passed string at import time.
/// This can be used for incrementally importable TRAP files -- use globally unique
/// strings to compute a unique ID for table tuples.
///
/// Note: You probably want to make sure that the key strings that you use are disjoint
/// for disjoint column types; the standard way of doing this is to prefix (or append)
/// the column type name to the ID. Thus, you might identify methods in Java by the
/// full ID "methods_com.method.package.DeclaringClass.method(argumentList)".
pub fn populate_empty_location(writer: &mut trap::Writer) {
let file_label = populate_empty_file(writer);
location(writer, file_label, 0, 0, 0, 0);
}
fn fresh_id(&mut self) -> Label {
let label = Label(self.counter);
self.counter += 1;
self.trap_output.push(TrapEntry::FreshId(label));
label
}
fn global_id(&mut self, key: &str) -> (Label, bool) {
if let Some(label) = self.global_keys.get(key) {
return (*label, false);
}
let label = Label(self.counter);
self.counter += 1;
self.global_keys.insert(key.to_owned(), label);
self.trap_output
.push(TrapEntry::MapLabelToKey(label, key.to_owned()));
(label, true)
}
fn add_tuple(&mut self, table_name: &str, args: Vec<Arg>) {
self.trap_output
.push(TrapEntry::GenericTuple(table_name.to_owned(), args))
}
fn populate_file(&mut self, absolute_path: &Path) -> Label {
let (file_label, fresh) = self.global_id(&full_id_for_file(absolute_path));
if fresh {
self.add_tuple(
"files",
vec![
Arg::Label(file_label),
Arg::String(normalize_path(absolute_path)),
],
);
self.populate_parent_folders(file_label, absolute_path.parent());
}
file_label
}
fn populate_empty_file(&mut self) -> Label {
let (file_label, fresh) = self.global_id("empty;sourcefile");
if fresh {
self.add_tuple(
"files",
vec![Arg::Label(file_label), Arg::String("".to_string())],
);
}
file_label
}
pub fn populate_empty_location(&mut self) {
let file_label = self.populate_empty_file();
self.location(file_label, 0, 0, 0, 0);
}
fn populate_parent_folders(&mut self, child_label: Label, path: Option<&Path>) {
let mut path = path;
let mut child_label = child_label;
loop {
match path {
None => break,
Some(folder) => {
let (folder_label, fresh) = self.global_id(&full_id_for_folder(folder));
self.add_tuple(
"containerparent",
vec![Arg::Label(folder_label), Arg::Label(child_label)],
pub fn populate_parent_folders(
writer: &mut trap::Writer,
child_label: trap::Label,
path: Option<&Path>,
) {
let mut path = path;
let mut child_label = child_label;
loop {
match path {
None => break,
Some(folder) => {
let (folder_label, fresh) =
writer.global_id(&trap::full_id_for_folder(&normalize_path(folder)));
writer.add_tuple(
"containerparent",
vec![
trap::Arg::Label(folder_label),
trap::Arg::Label(child_label),
],
);
if fresh {
writer.add_tuple(
"folders",
vec![
trap::Arg::Label(folder_label),
trap::Arg::String(normalize_path(folder)),
],
);
if fresh {
self.add_tuple(
"folders",
vec![
Arg::Label(folder_label),
Arg::String(normalize_path(folder)),
],
);
path = folder.parent();
child_label = folder_label;
} else {
break;
}
path = folder.parent();
child_label = folder_label;
} else {
break;
}
}
}
}
}
fn location(
&mut self,
file_label: Label,
start_line: usize,
start_column: usize,
end_line: usize,
end_column: usize,
) -> Label {
let (loc_label, fresh) = self.global_id(&format!(
"loc,{{{}}},{},{},{},{}",
file_label, start_line, start_column, end_line, end_column
));
if fresh {
self.add_tuple(
"locations_default",
vec![
Arg::Label(loc_label),
Arg::Label(file_label),
Arg::Int(start_line),
Arg::Int(start_column),
Arg::Int(end_line),
Arg::Int(end_column),
],
);
}
loc_label
}
fn comment(&mut self, text: String) {
self.trap_output.push(TrapEntry::Comment(text));
}
pub fn output(self, writer: &mut dyn Write) -> std::io::Result<()> {
write!(writer, "{}", Program(self.trap_output))
fn location(
writer: &mut trap::Writer,
file_label: trap::Label,
start_line: usize,
start_column: usize,
end_line: usize,
end_column: usize,
) -> trap::Label {
let (loc_label, fresh) = writer.global_id(&format!(
"loc,{{{}}},{},{},{},{}",
file_label, start_line, start_column, end_line, end_column
));
if fresh {
writer.add_tuple(
"locations_default",
vec![
trap::Arg::Label(loc_label),
trap::Arg::Label(file_label),
trap::Arg::Int(start_line),
trap::Arg::Int(start_column),
trap::Arg::Int(end_line),
trap::Arg::Int(end_column),
],
);
}
loc_label
}
/// Extracts the source file at `path`, which is assumed to be canonicalized.
@@ -163,71 +114,43 @@ pub fn extract(
language: Language,
language_prefix: &str,
schema: &NodeTypeMap,
trap_writer: &mut TrapWriter,
trap_writer: &mut trap::Writer,
path: &Path,
source: &[u8],
ranges: &[Range],
) -> std::io::Result<()> {
let path_str = format!("{}", path.display());
let span = span!(
Level::TRACE,
"extract",
file = %path.display()
file = %path_str
);
let _enter = span.enter();
info!("extracting: {}", path.display());
info!("extracting: {}", path_str);
let mut parser = Parser::new();
parser.set_language(language).unwrap();
parser.set_included_ranges(ranges).unwrap();
let tree = parser.parse(&source, None).expect("Failed to parse file");
trap_writer.comment(format!("Auto-generated TRAP file for {}", path.display()));
let file_label = &trap_writer.populate_file(path);
let mut visitor = Visitor {
trap_writer.comment(format!("Auto-generated TRAP file for {}", path_str));
let file_label = populate_file(trap_writer, path);
let mut visitor = Visitor::new(
source,
trap_writer,
// TODO: should we handle path strings that are not valid UTF8 better?
path: format!("{}", path.display()),
file_label: *file_label,
toplevel_child_counter: 0,
stack: Vec::new(),
&path_str,
file_label,
language_prefix,
schema,
};
);
traverse(&tree, &mut visitor);
parser.reset();
Ok(())
}
/// Escapes a string for use in a TRAP key, by replacing special characters with
/// HTML entities.
fn escape_key<'a, S: Into<Cow<'a, str>>>(key: S) -> Cow<'a, str> {
fn needs_escaping(c: char) -> bool {
matches!(c, '&' | '{' | '}' | '"' | '@' | '#')
}
let key = key.into();
if key.contains(needs_escaping) {
let mut escaped = String::with_capacity(2 * key.len());
for c in key.chars() {
match c {
'&' => escaped.push_str("&amp;"),
'{' => escaped.push_str("&lbrace;"),
'}' => escaped.push_str("&rbrace;"),
'"' => escaped.push_str("&quot;"),
'@' => escaped.push_str("&commat;"),
'#' => escaped.push_str("&num;"),
_ => escaped.push(c),
}
}
Cow::Owned(escaped)
} else {
key
}
}
/// Normalizes the path according the common CodeQL specification. Assumes that
/// `path` has already been canonicalized using `std::fs::canonicalize`.
fn normalize_path(path: &Path) -> String {
@@ -267,34 +190,28 @@ fn normalize_path(path: &Path) -> String {
}
}
fn full_id_for_file(path: &Path) -> String {
format!("{};sourcefile", escape_key(&normalize_path(path)))
}
fn full_id_for_folder(path: &Path) -> String {
format!("{};folder", escape_key(&normalize_path(path)))
}
struct ChildNode {
field_name: Option<&'static str>,
label: Label,
label: trap::Label,
type_name: TypeName,
}
struct Visitor<'a> {
/// The file path of the source code (as string)
path: String,
path: &'a str,
/// The label to use whenever we need to refer to the `@file` entity of this
/// source file.
file_label: Label,
file_label: trap::Label,
/// The source code as a UTF-8 byte array
source: &'a [u8],
/// A TrapWriter to accumulate trap entries
trap_writer: &'a mut TrapWriter,
/// A trap::Writer to accumulate trap entries
trap_writer: &'a mut trap::Writer,
/// A counter for top-level child nodes
toplevel_child_counter: usize,
/// Language prefix
language_prefix: &'a str,
/// Language-specific name of the AST info table
ast_node_info_table_name: String,
/// Language-specific name of the tokeninfo table
tokeninfo_table_name: String,
/// A lookup table from type name to node types
schema: &'a NodeTypeMap,
/// A stack for gathering information from child nodes. Whenever a node is
@@ -303,27 +220,48 @@ struct Visitor<'a> {
/// node the list containing the child data is popped from the stack and
/// matched against the dbscheme for the node. If the expectations are met
/// the corresponding row definitions are added to the trap_output.
stack: Vec<(Label, usize, Vec<ChildNode>)>,
stack: Vec<(trap::Label, usize, Vec<ChildNode>)>,
}
impl Visitor<'_> {
impl<'a> Visitor<'a> {
fn new(
source: &'a [u8],
trap_writer: &'a mut trap::Writer,
path: &'a str,
file_label: trap::Label,
language_prefix: &str,
schema: &'a NodeTypeMap,
) -> Visitor<'a> {
Visitor {
path,
file_label,
source,
trap_writer,
toplevel_child_counter: 0,
ast_node_info_table_name: format!("{}_ast_node_info", language_prefix),
tokeninfo_table_name: format!("{}_tokeninfo", language_prefix),
schema,
stack: Vec::new(),
}
}
fn record_parse_error(
&mut self,
error_message: String,
full_error_message: String,
loc: Label,
loc: trap::Label,
) {
error!("{}", full_error_message);
let id = self.trap_writer.fresh_id();
self.trap_writer.add_tuple(
"diagnostics",
vec![
Arg::Label(id),
Arg::Int(40), // severity 40 = error
Arg::String("parse_error".to_string()),
Arg::String(error_message),
Arg::String(full_error_message),
Arg::Label(loc),
trap::Arg::Label(id),
trap::Arg::Int(40), // severity 40 = error
trap::Arg::String("parse_error".to_string()),
trap::Arg::String(error_message),
trap::Arg::String(full_error_message),
trap::Arg::Label(loc),
],
);
}
@@ -335,7 +273,8 @@ impl Visitor<'_> {
node: Node,
) {
let (start_line, start_column, end_line, end_column) = location_for(self.source, node);
let loc = self.trap_writer.location(
let loc = location(
self.trap_writer,
self.file_label,
start_line,
start_column,
@@ -374,7 +313,8 @@ impl Visitor<'_> {
}
let (id, _, child_nodes) = self.stack.pop().expect("Vistor: empty stack");
let (start_line, start_column, end_line, end_column) = location_for(self.source, node);
let loc = self.trap_writer.location(
let loc = location(
self.trap_writer,
self.file_label,
start_line,
start_column,
@@ -402,19 +342,19 @@ impl Visitor<'_> {
match &table.kind {
EntryKind::Token { kind_id, .. } => {
self.trap_writer.add_tuple(
&format!("{}_ast_node_info", self.language_prefix),
&self.ast_node_info_table_name,
vec![
Arg::Label(id),
Arg::Label(parent_id),
Arg::Int(parent_index),
Arg::Label(loc),
trap::Arg::Label(id),
trap::Arg::Label(parent_id),
trap::Arg::Int(parent_index),
trap::Arg::Label(loc),
],
);
self.trap_writer.add_tuple(
&format!("{}_tokeninfo", self.language_prefix),
&self.tokeninfo_table_name,
vec![
Arg::Label(id),
Arg::Int(*kind_id),
trap::Arg::Label(id),
trap::Arg::Int(*kind_id),
sliced_source_arg(self.source, node),
],
);
@@ -425,15 +365,15 @@ impl Visitor<'_> {
} => {
if let Some(args) = self.complex_node(&node, fields, &child_nodes, id) {
self.trap_writer.add_tuple(
&format!("{}_ast_node_info", self.language_prefix),
&self.ast_node_info_table_name,
vec![
Arg::Label(id),
Arg::Label(parent_id),
Arg::Int(parent_index),
Arg::Label(loc),
trap::Arg::Label(id),
trap::Arg::Label(parent_id),
trap::Arg::Int(parent_index),
trap::Arg::Label(loc),
],
);
let mut all_args = vec![Arg::Label(id)];
let mut all_args = vec![trap::Arg::Label(id)];
all_args.extend(args);
self.trap_writer.add_tuple(table_name, all_args);
}
@@ -472,9 +412,9 @@ impl Visitor<'_> {
node: &Node,
fields: &[Field],
child_nodes: &[ChildNode],
parent_id: Label,
) -> Option<Vec<Arg>> {
let mut map: Map<&Option<String>, (&Field, Vec<Arg>)> = Map::new();
parent_id: trap::Label,
) -> Option<Vec<trap::Arg>> {
let mut map: Map<&Option<String>, (&Field, Vec<trap::Arg>)> = Map::new();
for field in fields {
map.insert(&field.name, (field, Vec::new()));
}
@@ -488,9 +428,9 @@ impl Visitor<'_> {
{
// We can safely unwrap because type_matches checks the key is in the map.
let (int_value, _) = int_mapping.get(&child_node.type_name.kind).unwrap();
values.push(Arg::Int(*int_value));
values.push(trap::Arg::Int(*int_value));
} else {
values.push(Arg::Label(child_node.label));
values.push(trap::Arg::Label(child_node.label));
}
} else if field.name.is_some() {
let error_message = format!(
@@ -569,9 +509,9 @@ impl Visitor<'_> {
);
break;
}
let mut args = vec![Arg::Label(parent_id)];
let mut args = vec![trap::Arg::Label(parent_id)];
if *has_index {
args.push(Arg::Int(index))
args.push(trap::Arg::Int(index))
}
args.push(child_value.clone());
self.trap_writer.add_tuple(table_name, args);
@@ -625,9 +565,9 @@ impl Visitor<'_> {
}
// Emit a slice of a source file as an Arg.
fn sliced_source_arg(source: &[u8], n: Node) -> Arg {
fn sliced_source_arg(source: &[u8], n: Node) -> trap::Arg {
let range = n.byte_range();
Arg::String(String::from_utf8_lossy(&source[range.start..range.end]).into_owned())
trap::Arg::String(String::from_utf8_lossy(&source[range.start..range.end]).into_owned())
}
// Emit a pair of `TrapEntry`s for the provided node, appropriately calibrated.
@@ -699,59 +639,6 @@ fn traverse(tree: &Tree, visitor: &mut Visitor) {
}
}
pub struct Program(Vec<TrapEntry>);
impl fmt::Display for Program {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut text = String::new();
for trap_entry in &self.0 {
text.push_str(&format!("{}\n", trap_entry));
}
write!(f, "{}", text)
}
}
enum TrapEntry {
/// Maps the label to a fresh id, e.g. `#123=*`.
FreshId(Label),
/// Maps the label to a key, e.g. `#7=@"foo"`.
MapLabelToKey(Label, String),
/// foo_bar(arg*)
GenericTuple(String, Vec<Arg>),
Comment(String),
}
impl fmt::Display for TrapEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TrapEntry::FreshId(label) => write!(f, "{}=*", label),
TrapEntry::MapLabelToKey(label, key) => {
write!(f, "{}=@\"{}\"", label, key.replace("\"", "\"\""))
}
TrapEntry::GenericTuple(name, args) => {
write!(f, "{}(", name)?;
for (index, arg) in args.iter().enumerate() {
if index > 0 {
write!(f, ",")?;
}
write!(f, "{}", arg)?;
}
write!(f, ")")
}
TrapEntry::Comment(line) => write!(f, "// {}", line),
}
}
}
#[derive(Debug, Copy, Clone)]
// Identifiers of the form #0, #1...
struct Label(u32);
impl fmt::Display for Label {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#{:x}", self.0)
}
}
// Numeric indices.
#[derive(Debug, Copy, Clone)]
struct Index(usize);
@@ -761,69 +648,3 @@ impl fmt::Display for Index {
write!(f, "{}", self.0)
}
}
// Some untyped argument to a TrapEntry.
#[derive(Debug, Clone)]
enum Arg {
Label(Label),
Int(usize),
String(String),
}
const MAX_STRLEN: usize = 1048576;
impl fmt::Display for Arg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Arg::Label(x) => write!(f, "{}", x),
Arg::Int(x) => write!(f, "{}", x),
Arg::String(x) => write!(
f,
"\"{}\"",
limit_string(x, MAX_STRLEN).replace("\"", "\"\"")
),
}
}
}
/// Limit the length (in bytes) of a string. If the string's length in bytes is
/// less than or equal to the limit then the entire string is returned. Otherwise
/// the string is sliced at the provided limit. If there is a multi-byte character
/// at the limit then the returned slice will be slightly shorter than the limit to
/// avoid splitting that multi-byte character.
fn limit_string(string: &str, max_size: usize) -> &str {
if string.len() <= max_size {
return string;
}
let p = string.as_bytes();
let mut index = max_size;
// We want to clip the string at [max_size]; however, the character at that position
// may span several bytes. We need to find the first byte of the character. In UTF-8
// encoded data any byte that matches the bit pattern 10XXXXXX is not a start byte.
// Therefore we decrement the index as long as there are bytes matching this pattern.
// This ensures we cut the string at the border between one character and another.
while index > 0 && (p[index] & 0b11000000) == 0b10000000 {
index -= 1;
}
&string[0..index]
}
#[test]
fn limit_string_test() {
assert_eq!("hello", limit_string(&"hello world".to_owned(), 5));
assert_eq!("hi ☹", limit_string(&"hi ☹☹".to_owned(), 6));
assert_eq!("hi ", limit_string(&"hi ☹☹".to_owned(), 5));
}
#[test]
fn escape_key_test() {
assert_eq!("foo!", escape_key("foo!"));
assert_eq!("foo&lbrace;&rbrace;", escape_key("foo{}"));
assert_eq!("&lbrace;&rbrace;", escape_key("{}"));
assert_eq!("", escape_key(""));
assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb"));
assert_eq!(
"/path/to/foo&amp;&lbrace;&rbrace;&quot;&commat;&num;.rb",
escape_key("/path/to/foo&{}\"@#.rb")
);
}

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