mirror of
https://github.com/github/codeql.git
synced 2026-04-27 09:45:15 +02:00
Merge branch 'main' into redsun82/swift-file-label-caching
This commit is contained in:
@@ -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
|
||||
|
||||
4
cpp/ql/lib/change-notes/2022-06-24-unique-variable.md
Normal file
4
cpp/ql/lib/change-notes/2022-06-24-unique-variable.md
Normal 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`.
|
||||
@@ -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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.3.0
|
||||
lastReleaseVersion: 0.3.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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(_)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.2.0
|
||||
lastReleaseVersion: 0.3.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.2.1-dev
|
||||
version: 0.3.1-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
## 1.2.1
|
||||
|
||||
## 1.2.0
|
||||
|
||||
## 1.1.4
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
## 1.2.1
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.2.0
|
||||
lastReleaseVersion: 1.2.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.2.1-dev
|
||||
version: 1.2.2-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
## 1.2.1
|
||||
|
||||
## 1.2.0
|
||||
|
||||
## 1.1.4
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
## 1.2.1
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.2.0
|
||||
lastReleaseVersion: 1.2.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.2.1-dev
|
||||
version: 1.2.2-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
## 0.3.1
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
1
csharp/ql/lib/change-notes/released/0.3.1.md
Normal file
1
csharp/ql/lib/change-notes/released/0.3.1.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.3.1
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.3.0
|
||||
lastReleaseVersion: 0.3.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.2.0
|
||||
lastReleaseVersion: 0.3.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 0.2.1-dev
|
||||
version: 0.3.1-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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::
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
## 0.2.1
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
1
go/ql/lib/change-notes/released/0.2.1.md
Normal file
1
go/ql/lib/change-notes/released/0.2.1.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.2.1
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.2.0
|
||||
lastReleaseVersion: 0.2.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
## 0.2.1
|
||||
|
||||
## 0.2.0
|
||||
|
||||
## 0.1.4
|
||||
|
||||
1
go/ql/src/change-notes/released/0.2.1.md
Normal file
1
go/ql/src/change-notes/released/0.2.1.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.2.1
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.2.0
|
||||
lastReleaseVersion: 0.2.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/go-queries
|
||||
version: 0.2.1-dev
|
||||
version: 0.2.2-dev
|
||||
groups:
|
||||
- go
|
||||
- queries
|
||||
|
||||
@@ -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 -> {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
Added additional flow sources for uses of external storage on Android.
|
||||
@@ -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.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
Added `Modifier.isInline()`.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
4
java/ql/lib/change-notes/2022-07-26-scanner-models.md
Normal file
4
java/ql/lib/change-notes/2022-07-26-scanner-models.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added data flow models for `java.util.Scanner`.
|
||||
12
java/ql/lib/change-notes/released/0.3.1.md
Normal file
12
java/ql/lib/change-notes/released/0.3.1.md
Normal 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.3.0
|
||||
lastReleaseVersion: 0.3.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* Contextual queries and the query libraries they depend on have been moved to the `codeql/java-all` package.
|
||||
11
java/ql/src/change-notes/released/0.3.0.md
Normal file
11
java/ql/src/change-notes/released/0.3.0.md
Normal 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.2.0
|
||||
lastReleaseVersion: 0.3.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-queries
|
||||
version: 0.2.1-dev
|
||||
version: 0.3.1-dev
|
||||
groups:
|
||||
- java
|
||||
- queries
|
||||
|
||||
@@ -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(...) |
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import java
|
||||
|
||||
from MethodAccess ma
|
||||
select ma
|
||||
@@ -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(...)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
public class Test {
|
||||
|
||||
public char f(String s) { return s.charAt(0); }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
| Test.java:3:36:3:46 | charAt(...) |
|
||||
| test.kt:2:20:2:23 | charAt(...) |
|
||||
2
java/ql/test/kotlin/library-tests/string-charat/test.kt
Normal file
2
java/ql/test/kotlin/library-tests/string-charat/test.kt
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
fun f(x: String) = x[0]
|
||||
4
java/ql/test/kotlin/library-tests/string-charat/test.ql
Normal file
4
java/ql/test/kotlin/library-tests/string-charat/test.ql
Normal file
@@ -0,0 +1,4 @@
|
||||
import java
|
||||
|
||||
from MethodAccess ma
|
||||
select ma
|
||||
328
java/ql/test/library-tests/scanner/Test.java
Normal file
328
java/ql/test/library-tests/scanner/Test.java
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
2
java/ql/test/library-tests/scanner/test.ql
Normal file
2
java/ql/test/library-tests/scanner/test.ql
Normal file
@@ -0,0 +1,2 @@
|
||||
import java
|
||||
import TestUtilities.InlineFlowTest
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query.
|
||||
@@ -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).
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `chownr` library is now modeled as a sink for the `js/path-injection` query.
|
||||
7
javascript/ql/lib/change-notes/released/0.2.1.md
Normal file
7
javascript/ql/lib/change-notes/released/0.2.1.md
Normal 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.2.0
|
||||
lastReleaseVersion: 0.2.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.2.0
|
||||
lastReleaseVersion: 0.3.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-queries
|
||||
version: 0.2.1-dev
|
||||
version: 0.3.1-dev
|
||||
groups:
|
||||
- javascript
|
||||
- queries
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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).
|
||||
@@ -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).
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.0
|
||||
lastReleaseVersion: 0.5.1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
category: breaking
|
||||
---
|
||||
* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package.
|
||||
|
||||
5
python/ql/src/change-notes/released/0.3.0.md
Normal file
5
python/ql/src/change-notes/released/0.3.0.md
Normal 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.2.0
|
||||
lastReleaseVersion: 0.3.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-queries
|
||||
version: 0.2.1-dev
|
||||
version: 0.3.1-dev
|
||||
groups:
|
||||
- python
|
||||
- queries
|
||||
|
||||
@@ -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("&"),
|
||||
'{' => escaped.push_str("{"),
|
||||
'}' => escaped.push_str("}"),
|
||||
'"' => escaped.push_str("""),
|
||||
'@' => escaped.push_str("@"),
|
||||
'#' => escaped.push_str("#"),
|
||||
_ => 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{}", escape_key("foo{}"));
|
||||
assert_eq!("{}", escape_key("{}"));
|
||||
assert_eq!("", escape_key(""));
|
||||
assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb"));
|
||||
assert_eq!(
|
||||
"/path/to/foo&{}"@#.rb",
|
||||
escape_key("/path/to/foo&{}\"@#.rb")
|
||||
);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user