Merge branch 'master' into init-dynamic-alloc-newexpr

This commit is contained in:
Mathias Vorreiter Pedersen
2020-04-02 14:11:03 +02:00
120 changed files with 1490 additions and 314 deletions

3
.gitignore vendored
View File

@@ -14,6 +14,9 @@
.vs/*
!.vs/VSWorkspaceSettings.json
# Byte-compiled python files
*.pyc
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
/codeql/
.vscode/settings.json

View File

@@ -20,6 +20,7 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
| Memory may not be freed (`cpp/memory-may-not-be-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
| Mismatching new/free or malloc/delete (`cpp/new-free-mismatch`) | Fewer false positive results | Fixed false positive results in template code. |
| Missing return statement (`cpp/missing-return`) | Fewer false positive results | Functions containing `asm` statements are no longer highlighted by this query. |
| Missing return statement (`cpp/missing-return`) | More accurate locations | Locations reported by this query are now more accurate in some cases. |
| No space for zero terminator (`cpp/no-space-for-terminator`) | More correct results | String arguments to formatting functions are now (usually) expected to be null terminated strings. |
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | | This query is no longer run on LGTM. |
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | This query has been modified to be more conservative when identifying which pointers point to null-terminated strings. This approach produces fewer, more accurate results. |

140
config/sync-files.py Normal file
View File

@@ -0,0 +1,140 @@
#!/usr/bin/env python3
# Due to various technical limitations, we sometimes have files that need to be
# kept identical in the repository. This script loads a database of such
# files and can perform two functions: check whether they are still identical,
# and overwrite the others with a master copy if needed.
import hashlib
import shutil
import os
import sys
import json
import re
path = os.path
file_groups = {}
def add_prefix(prefix, relative):
result = path.join(prefix, relative)
if path.commonprefix((path.realpath(result), path.realpath(prefix))) != \
path.realpath(prefix):
raise Exception("Path {} is not below {}".format(
result, prefix))
return result
def load_if_exists(prefix, json_file_relative):
json_file_name = path.join(prefix, json_file_relative)
if path.isfile(json_file_name):
print("Loading file groups from", json_file_name)
with open(json_file_name, 'r', encoding='utf-8') as fp:
raw_groups = json.load(fp)
prefixed_groups = {
name: [
add_prefix(prefix, relative)
for relative in relatives
]
for name, relatives in raw_groups.items()
}
file_groups.update(prefixed_groups)
# Generates a list of C# test files that should be in sync
def csharp_test_files():
test_file_re = re.compile('.*(Bad|Good)[0-9]*\\.cs$')
csharp_doc_files = {
file:os.path.join(root, file)
for root, dirs, files in os.walk("csharp/ql/src")
for file in files
if test_file_re.match(file)
}
return {
"C# test '" + file + "'" : [os.path.join(root, file), csharp_doc_files[file]]
for root, dirs, files in os.walk("csharp/ql/test")
for file in files
if file in csharp_doc_files
}
def file_checksum(filename):
with open(filename, 'rb') as file_handle:
return hashlib.sha1(file_handle.read()).hexdigest()
def check_group(group_name, files, master_file_picker, emit_error):
checksums = {file_checksum(f) for f in files}
if len(checksums) == 1:
return
master_file = master_file_picker(files)
if master_file is None:
emit_error(__file__, 0,
"Files from group '"+ group_name +"' not in sync.")
emit_error(__file__, 0,
"Run this script with a file-name argument among the "
"following to overwrite the remaining files with the contents "
"of that file or run with the --latest switch to update each "
"group of files from the most recently modified file in the group.")
for filename in files:
emit_error(__file__, 0, " " + filename)
else:
print(" Syncing others from", master_file)
for filename in files:
if filename == master_file:
continue
print(" " + filename)
os.replace(filename, filename + '~')
shutil.copy(master_file, filename)
print(" Backups written with '~' appended to file names")
def chdir_repo_root():
root_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
os.chdir(root_path)
def choose_master_file(master_file, files):
if master_file in files:
return master_file
else:
return None
def choose_latest_file(files):
latest_time = None
latest_file = None
for filename in files:
file_time = os.path.getmtime(filename)
if (latest_time is None) or (latest_time < file_time):
latest_time = file_time
latest_file = filename
return latest_file
local_error_count = 0
def emit_local_error(path, line, error):
print('ERROR: ' + path + ':' + line + " - " + error)
global local_error_count
local_error_count += 1
# This function is invoked directly by a CI script, which passes a different error-handling
# callback.
def sync_identical_files(emit_error):
if len(sys.argv) == 1:
master_file_picker = lambda files: None
elif len(sys.argv) == 2:
if sys.argv[1] == "--latest":
master_file_picker = choose_latest_file
elif os.path.isfile(sys.argv[1]):
master_file_picker = lambda files: choose_master_file(sys.argv[1], files)
else:
raise Exception("File not found")
else:
raise Exception("Bad command line or file not found")
chdir_repo_root()
load_if_exists('.', 'config/identical-files.json')
file_groups.update(csharp_test_files())
for group_name, files in file_groups.items():
check_group(group_name, files, master_file_picker, emit_error)
def main():
sync_identical_files(emit_local_error)
if local_error_count > 0:
exit(1)
if __name__ == "__main__":
main()

View File

@@ -30,7 +30,13 @@ predicate functionsMissingReturnStmt(Function f, ControlFlowNode blame) {
) and
exists(ReturnStmt s |
f.getAPredecessor() = s and
blame = s.getAPredecessor()
(
blame = s.getAPredecessor() and
count(blame.getASuccessor()) = 1
or
blame = s and
exists(ControlFlowNode pred | pred = s.getAPredecessor() | count(pred.getASuccessor()) != 1)
)
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2,6 +2,7 @@ import semmle.code.cpp.Element
private import semmle.code.cpp.Enclosing
private import semmle.code.cpp.internal.ResolveClass
private import semmle.code.cpp.internal.AddressConstantExpression
private import semmle.code.cpp.models.implementations.Allocation
/**
* A C/C++ expression.
@@ -804,8 +805,10 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
* call the constructor of `T` but will not allocate memory.
*/
Expr getPlacementPointer() {
isStandardPlacementNewAllocator(this.getAllocator()) and
result = this.getAllocatorCall().getArgument(1)
result =
this
.getAllocatorCall()
.getArgument(this.getAllocator().(OperatorNewAllocationFunction).getPlacementArgument())
}
}
@@ -1194,12 +1197,6 @@ private predicate convparents(Expr child, int idx, Element parent) {
)
}
private predicate isStandardPlacementNewAllocator(Function operatorNew) {
operatorNew.getName().matches("operator new%") and
operatorNew.getNumberOfParameters() = 2 and
operatorNew.getParameter(1).getType() instanceof VoidPointerType
}
// Pulled out for performance. See QL-796.
private predicate hasNoConversions(Expr e) { not e.hasConversion() }

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -215,6 +215,40 @@ class SizelessAllocationFunction extends AllocationFunction {
}
}
/**
* An `operator new` or `operator new[]` function that may be associated with `new` or
* `new[]` expressions. Note that `new` and `new[]` are not function calls, but these
* functions may also be called directly.
*/
class OperatorNewAllocationFunction extends AllocationFunction {
OperatorNewAllocationFunction() {
exists(string name |
hasGlobalName(name) and
(
// operator new(bytes, ...)
name = "operator new"
or
// operator new[](bytes, ...)
name = "operator new[]"
)
)
}
override int getSizeArg() { result = 0 }
override predicate requiresDealloc() { not exists(getPlacementArgument()) }
/**
* Gets the position of the placement pointer if this is a placement
* `operator new` function.
*/
int getPlacementArgument() {
getNumberOfParameters() = 2 and
getParameter(1).getType() instanceof VoidPointerType and
result = 1
}
}
/**
* An allocation expression that is a function call, such as call to `malloc`.
*/
@@ -227,7 +261,9 @@ class CallAllocationExpr extends AllocationExpr, FunctionCall {
not (
exists(target.getReallocPtrArg()) and
getArgument(target.getSizeArg()).getValue().toInt() = 0
)
) and
// these are modelled directly (and more accurately), avoid duplication
not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this)
}
override Expr getSizeExpr() { result = getArgument(target.getSizeArg()) }

View File

@@ -79,6 +79,28 @@ class StandardDeallocationFunction extends DeallocationFunction {
override int getFreedArg() { result = freedArg }
}
/**
* An `operator delete` or `operator delete[]` function that may be associated
* with `delete` or `delete[]` expressions. Note that `delete` and `delete[]`
* are not function calls, but these functions may also be called directly.
*/
class OperatorDeleteDeallocationFunction extends DeallocationFunction {
OperatorDeleteDeallocationFunction() {
exists(string name |
hasGlobalName(name) and
(
// operator delete(pointer, ...)
name = "operator delete"
or
// operator delete[](pointer, ...)
name = "operator delete[]"
)
)
}
override int getFreedArg() { result = 0 }
}
/**
* An deallocation expression that is a function call, such as call to `free`.
*/

View File

@@ -1,11 +1,12 @@
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.SideEffect
/**
* The standard function `strcat` and its wide, sized, and Microsoft variants.
*/
class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, SideEffectFunction {
StrcatFunction() {
exists(string name | name = getName() |
name = "strcat" or // strcat(dst, src)
@@ -56,4 +57,19 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
override predicate hasArrayWithNullTerminator(int param) { param = 1 }
override predicate hasArrayWithUnknownSize(int param) { param = 0 }
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i = 0 and
buffer = true and
mustWrite = false
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
(i = 0 or i = 1) and
buffer = true
}
}

View File

@@ -1,11 +1,12 @@
import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.SideEffect
/**
* The standard function `strcpy` and its wide, sized, and Microsoft variants.
*/
class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, SideEffectFunction {
StrcpyFunction() {
this.hasName("strcpy") or
this.hasName("_mbscpy") or
@@ -74,4 +75,23 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
output.isReturnValueDeref()
)
}
override predicate hasOnlySpecificReadSideEffects() { any() }
override predicate hasOnlySpecificWriteSideEffects() { any() }
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
i = 0 and
buffer = true and
mustWrite = false
}
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
i = 1 and
buffer = true
}
override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
hasArrayWithVariableSize(i, result)
}
}

View File

@@ -143,3 +143,9 @@ void multidimensionalNew(int x, int y) {
auto p2 = new char[20][20];
auto p3 = new char[x][30][30];
}
void directOperatorCall() {
void *ptr;
ptr = operator new(sizeof(int));
operator delete(ptr);
}

View File

@@ -1,28 +1,28 @@
newExprs
| allocators.cpp:49:3:49:9 | new | int | operator new(unsigned long) -> void * | 4 | 4 | |
| allocators.cpp:50:3:50:15 | new | int | operator new(size_t, float) -> void * | 4 | 4 | |
| allocators.cpp:51:3:51:11 | new | int | operator new(unsigned long) -> void * | 4 | 4 | |
| allocators.cpp:52:3:52:14 | new | String | operator new(unsigned long) -> void * | 8 | 8 | |
| allocators.cpp:53:3:53:27 | new | String | operator new(size_t, float) -> void * | 8 | 8 | |
| allocators.cpp:54:3:54:17 | new | Overaligned | operator new(unsigned long, align_val_t) -> void * | 256 | 128 | aligned |
| allocators.cpp:55:3:55:25 | new | Overaligned | operator new(size_t, align_val_t, float) -> void * | 256 | 128 | aligned |
| allocators.cpp:107:3:107:18 | new | FailedInit | FailedInit::operator new(size_t) -> void * | 1 | 1 | |
| allocators.cpp:109:3:109:35 | new | FailedInitOveraligned | FailedInitOveraligned::operator new(size_t, align_val_t, float) -> void * | 128 | 128 | aligned |
| allocators.cpp:129:3:129:21 | new | int | operator new(size_t, void *) -> void * | 4 | 4 | |
| allocators.cpp:135:3:135:26 | new | int | operator new(size_t, const nothrow_t &) -> void * | 4 | 4 | |
| allocators.cpp:49:3:49:9 | new | int | operator new(unsigned long) -> void * | 4 | 4 | | |
| allocators.cpp:50:3:50:15 | new | int | operator new(size_t, float) -> void * | 4 | 4 | | |
| allocators.cpp:51:3:51:11 | new | int | operator new(unsigned long) -> void * | 4 | 4 | | |
| allocators.cpp:52:3:52:14 | new | String | operator new(unsigned long) -> void * | 8 | 8 | | |
| allocators.cpp:53:3:53:27 | new | String | operator new(size_t, float) -> void * | 8 | 8 | | |
| allocators.cpp:54:3:54:17 | new | Overaligned | operator new(unsigned long, align_val_t) -> void * | 256 | 128 | aligned | |
| allocators.cpp:55:3:55:25 | new | Overaligned | operator new(size_t, align_val_t, float) -> void * | 256 | 128 | aligned | |
| allocators.cpp:107:3:107:18 | new | FailedInit | FailedInit::operator new(size_t) -> void * | 1 | 1 | | |
| allocators.cpp:109:3:109:35 | new | FailedInitOveraligned | FailedInitOveraligned::operator new(size_t, align_val_t, float) -> void * | 128 | 128 | aligned | |
| allocators.cpp:129:3:129:21 | new | int | operator new(size_t, void *) -> void * | 4 | 4 | | & ... |
| allocators.cpp:135:3:135:26 | new | int | operator new(size_t, const nothrow_t &) -> void * | 4 | 4 | | |
newArrayExprs
| allocators.cpp:68:3:68:12 | new[] | int[] | int | operator new[](unsigned long) -> void * | 4 | 4 | | n |
| allocators.cpp:69:3:69:18 | new[] | int[] | int | operator new[](size_t, float) -> void * | 4 | 4 | | n |
| allocators.cpp:70:3:70:15 | new[] | String[] | String | operator new[](unsigned long) -> void * | 8 | 8 | | n |
| allocators.cpp:71:3:71:20 | new[] | Overaligned[] | Overaligned | operator new[](unsigned long, align_val_t) -> void * | 256 | 128 | aligned | n |
| allocators.cpp:72:3:72:16 | new[] | String[10] | String | operator new[](unsigned long) -> void * | 8 | 8 | | |
| allocators.cpp:108:3:108:19 | new[] | FailedInit[] | FailedInit | FailedInit::operator new[](size_t) -> void * | 1 | 1 | | n |
| allocators.cpp:110:3:110:37 | new[] | FailedInitOveraligned[10] | FailedInitOveraligned | FailedInitOveraligned::operator new[](size_t, align_val_t, float) -> void * | 128 | 128 | aligned | |
| allocators.cpp:132:3:132:17 | new[] | int[1] | int | operator new[](size_t, void *) -> void * | 4 | 4 | | |
| allocators.cpp:136:3:136:26 | new[] | int[2] | int | operator new[](size_t, const nothrow_t &) -> void * | 4 | 4 | | |
| allocators.cpp:142:13:142:27 | new[] | char[][10] | char[10] | operator new[](unsigned long) -> void * | 10 | 1 | | x |
| allocators.cpp:143:13:143:28 | new[] | char[20][20] | char[20] | operator new[](unsigned long) -> void * | 20 | 1 | | |
| allocators.cpp:144:13:144:31 | new[] | char[][30][30] | char[30][30] | operator new[](unsigned long) -> void * | 900 | 1 | | x |
| allocators.cpp:68:3:68:12 | new[] | int[] | int | operator new[](unsigned long) -> void * | 4 | 4 | | n | |
| allocators.cpp:69:3:69:18 | new[] | int[] | int | operator new[](size_t, float) -> void * | 4 | 4 | | n | |
| allocators.cpp:70:3:70:15 | new[] | String[] | String | operator new[](unsigned long) -> void * | 8 | 8 | | n | |
| allocators.cpp:71:3:71:20 | new[] | Overaligned[] | Overaligned | operator new[](unsigned long, align_val_t) -> void * | 256 | 128 | aligned | n | |
| allocators.cpp:72:3:72:16 | new[] | String[10] | String | operator new[](unsigned long) -> void * | 8 | 8 | | | |
| allocators.cpp:108:3:108:19 | new[] | FailedInit[] | FailedInit | FailedInit::operator new[](size_t) -> void * | 1 | 1 | | n | |
| allocators.cpp:110:3:110:37 | new[] | FailedInitOveraligned[10] | FailedInitOveraligned | FailedInitOveraligned::operator new[](size_t, align_val_t, float) -> void * | 128 | 128 | aligned | | |
| allocators.cpp:132:3:132:17 | new[] | int[1] | int | operator new[](size_t, void *) -> void * | 4 | 4 | | | buf |
| allocators.cpp:136:3:136:26 | new[] | int[2] | int | operator new[](size_t, const nothrow_t &) -> void * | 4 | 4 | | | |
| allocators.cpp:142:13:142:27 | new[] | char[][10] | char[10] | operator new[](unsigned long) -> void * | 10 | 1 | | x | |
| allocators.cpp:143:13:143:28 | new[] | char[20][20] | char[20] | operator new[](unsigned long) -> void * | 20 | 1 | | | |
| allocators.cpp:144:13:144:31 | new[] | char[][30][30] | char[30][30] | operator new[](unsigned long) -> void * | 900 | 1 | | x | |
newExprDeallocators
| allocators.cpp:52:3:52:14 | new | String | operator delete(void *, unsigned long) -> void | 8 | 8 | sized |
| allocators.cpp:53:3:53:27 | new | String | operator delete(void *, float) -> void | 8 | 8 | |
@@ -46,3 +46,65 @@ deleteArrayExprs
| allocators.cpp:81:3:81:45 | delete[] | Overaligned | operator delete[](void *, unsigned long, align_val_t) -> void | 256 | 128 | sized aligned |
| allocators.cpp:82:3:82:49 | delete[] | PolymorphicBase | operator delete[](void *, unsigned long) -> void | 8 | 8 | sized |
| allocators.cpp:83:3:83:23 | delete[] | int | operator delete[](void *, unsigned long) -> void | 4 | 4 | sized |
allocationFunctions
| allocators.cpp:7:7:7:18 | operator new | getSizeArg = 0, requiresDealloc |
| allocators.cpp:8:7:8:20 | operator new[] | getSizeArg = 0, requiresDealloc |
| allocators.cpp:9:7:9:18 | operator new | getSizeArg = 0, requiresDealloc |
| allocators.cpp:10:7:10:20 | operator new[] | getSizeArg = 0, requiresDealloc |
| allocators.cpp:121:7:121:18 | operator new | getPlacementArgument = 1, getSizeArg = 0 |
| allocators.cpp:122:7:122:20 | operator new[] | getPlacementArgument = 1, getSizeArg = 0 |
| allocators.cpp:123:7:123:18 | operator new | getSizeArg = 0, requiresDealloc |
| allocators.cpp:124:7:124:20 | operator new[] | getSizeArg = 0, requiresDealloc |
| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc |
| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc |
| file://:0:0:0:0 | operator new[] | getSizeArg = 0, requiresDealloc |
| file://:0:0:0:0 | operator new[] | getSizeArg = 0, requiresDealloc |
allocationExprs
| allocators.cpp:49:3:49:9 | new | getSizeBytes = 4, requiresDealloc |
| allocators.cpp:50:3:50:15 | new | getSizeBytes = 4, requiresDealloc |
| allocators.cpp:51:3:51:11 | new | getSizeBytes = 4, requiresDealloc |
| allocators.cpp:52:3:52:14 | new | getSizeBytes = 8, requiresDealloc |
| allocators.cpp:53:3:53:27 | new | getSizeBytes = 8, requiresDealloc |
| allocators.cpp:54:3:54:17 | new | getSizeBytes = 256, requiresDealloc |
| allocators.cpp:55:3:55:25 | new | getSizeBytes = 256, requiresDealloc |
| allocators.cpp:68:3:68:12 | new[] | getSizeExpr = n, getSizeMult = 4, requiresDealloc |
| allocators.cpp:69:3:69:18 | new[] | getSizeExpr = n, getSizeMult = 4, requiresDealloc |
| allocators.cpp:70:3:70:15 | new[] | getSizeExpr = n, getSizeMult = 8, requiresDealloc |
| allocators.cpp:71:3:71:20 | new[] | getSizeExpr = n, getSizeMult = 256, requiresDealloc |
| allocators.cpp:72:3:72:16 | new[] | getSizeBytes = 80, requiresDealloc |
| allocators.cpp:107:3:107:18 | new | getSizeBytes = 1, requiresDealloc |
| allocators.cpp:108:3:108:19 | new[] | getSizeExpr = n, getSizeMult = 1, requiresDealloc |
| allocators.cpp:109:3:109:35 | new | getSizeBytes = 128, requiresDealloc |
| allocators.cpp:110:3:110:37 | new[] | getSizeBytes = 1280, requiresDealloc |
| allocators.cpp:129:3:129:21 | new | getSizeBytes = 4 |
| allocators.cpp:132:3:132:17 | new[] | getSizeBytes = 4 |
| allocators.cpp:135:3:135:26 | new | getSizeBytes = 4, requiresDealloc |
| allocators.cpp:136:3:136:26 | new[] | getSizeBytes = 8, requiresDealloc |
| allocators.cpp:142:13:142:27 | new[] | getSizeExpr = x, getSizeMult = 10, requiresDealloc |
| allocators.cpp:143:13:143:28 | new[] | getSizeBytes = 400, requiresDealloc |
| allocators.cpp:144:13:144:31 | new[] | getSizeExpr = x, getSizeMult = 900, requiresDealloc |
| allocators.cpp:149:8:149:19 | call to operator new | getSizeBytes = 4, getSizeExpr = sizeof(int), getSizeMult = 1, requiresDealloc |
deallocationFunctions
| allocators.cpp:11:6:11:20 | operator delete | getFreedArg = 0 |
| allocators.cpp:12:6:12:22 | operator delete[] | getFreedArg = 0 |
| allocators.cpp:13:6:13:20 | operator delete | getFreedArg = 0 |
| allocators.cpp:14:6:14:22 | operator delete[] | getFreedArg = 0 |
| file://:0:0:0:0 | operator delete | getFreedArg = 0 |
| file://:0:0:0:0 | operator delete | getFreedArg = 0 |
| file://:0:0:0:0 | operator delete | getFreedArg = 0 |
| file://:0:0:0:0 | operator delete[] | getFreedArg = 0 |
| file://:0:0:0:0 | operator delete[] | getFreedArg = 0 |
deallocationExprs
| allocators.cpp:59:3:59:35 | delete | getFreedExpr = 0 |
| allocators.cpp:60:3:60:38 | delete | getFreedExpr = 0 |
| allocators.cpp:61:3:61:44 | delete | getFreedExpr = 0 |
| allocators.cpp:62:3:62:43 | delete | getFreedExpr = 0 |
| allocators.cpp:63:3:63:47 | delete | getFreedExpr = 0 |
| allocators.cpp:64:3:64:44 | delete | getFreedExpr = 0 |
| allocators.cpp:78:3:78:37 | delete[] | getFreedExpr = 0 |
| allocators.cpp:79:3:79:40 | delete[] | getFreedExpr = 0 |
| allocators.cpp:80:3:80:46 | delete[] | getFreedExpr = 0 |
| allocators.cpp:81:3:81:45 | delete[] | getFreedExpr = 0 |
| allocators.cpp:82:3:82:49 | delete[] | getFreedExpr = 0 |
| allocators.cpp:83:3:83:23 | delete[] | getFreedExpr = call to GetPointer |
| allocators.cpp:150:2:150:16 | call to operator delete | getFreedExpr = ptr |

View File

@@ -1,6 +1,9 @@
import default
import semmle.code.cpp.models.implementations.Allocation
query predicate newExprs(NewExpr expr, string type, string sig, int size, int alignment, string form) {
query predicate newExprs(
NewExpr expr, string type, string sig, int size, int alignment, string form, string placement
) {
exists(Function allocator, Type allocatedType |
expr.getAllocator() = allocator and
sig = allocator.getFullSignature() and
@@ -8,13 +11,16 @@ query predicate newExprs(NewExpr expr, string type, string sig, int size, int al
type = allocatedType.toString() and
size = allocatedType.getSize() and
alignment = allocatedType.getAlignment() and
if expr.hasAlignedAllocation() then form = "aligned" else form = ""
(if expr.hasAlignedAllocation() then form = "aligned" else form = "") and
if exists(expr.getPlacementPointer())
then placement = expr.getPlacementPointer().toString()
else placement = ""
)
}
query predicate newArrayExprs(
NewArrayExpr expr, string t1, string t2, string sig, int size, int alignment, string form,
string extents
string extents, string placement
) {
exists(Function allocator, Type arrayType, Type elementType |
expr.getAllocator() = allocator and
@@ -26,7 +32,10 @@ query predicate newArrayExprs(
size = elementType.getSize() and
alignment = elementType.getAlignment() and
(if expr.hasAlignedAllocation() then form = "aligned" else form = "") and
extents = concat(Expr e | e = expr.getExtent() | e.toString(), ", ")
extents = concat(Expr e | e = expr.getExtent() | e.toString(), ", ") and
if exists(expr.getPlacementPointer())
then placement = expr.getPlacementPointer().toString()
else placement = ""
)
}
@@ -101,3 +110,54 @@ query predicate deleteArrayExprs(
)
)
}
string describeAllocationFunction(AllocationFunction f) {
result = "getSizeArg = " + f.getSizeArg().toString()
or
result = "getSizeMult = " + f.getSizeMult().toString()
or
result = "getReallocPtrArg = " + f.getReallocPtrArg().toString()
or
f.requiresDealloc() and
result = "requiresDealloc"
or
result =
"getPlacementArgument = " + f.(OperatorNewAllocationFunction).getPlacementArgument().toString()
}
query predicate allocationFunctions(AllocationFunction f, string descr) {
descr = concat(describeAllocationFunction(f), ", ")
}
string describeAllocationExpr(AllocationExpr e) {
result = "getSizeExpr = " + e.getSizeExpr().toString()
or
result = "getSizeMult = " + e.getSizeMult().toString()
or
result = "getSizeBytes = " + e.getSizeBytes().toString()
or
result = "getReallocPtr = " + e.getReallocPtr().toString()
or
e.requiresDealloc() and
result = "requiresDealloc"
}
query predicate allocationExprs(AllocationExpr e, string descr) {
descr = concat(describeAllocationExpr(e), ", ")
}
string describeDeallocationFunction(DeallocationFunction f) {
result = "getFreedArg = " + f.getFreedArg().toString()
}
query predicate deallocationFunctions(DeallocationFunction f, string descr) {
descr = concat(describeDeallocationFunction(f), ", ")
}
string describeDeallocationExpr(DeallocationExpr e) {
result = "getFreedExpr = " + e.getFreedExpr().toString()
}
query predicate deallocationExprs(DeallocationExpr e, string descr) {
descr = concat(describeDeallocationExpr(e), ", ")
}

View File

@@ -1,2 +0,0 @@
| allocators.cpp:129:3:129:21 | new | allocators.cpp:129:7:129:13 | & ... |
| allocators.cpp:132:3:132:17 | new[] | allocators.cpp:132:7:132:9 | buf |

View File

@@ -1,4 +0,0 @@
import cpp
from NewOrNewArrayExpr new
select new, new.getPlacementPointer() as placement

View File

@@ -512,3 +512,20 @@
| taint.cpp:444:7:444:7 | d [post update] | taint.cpp:447:7:447:7 | d | |
| taint.cpp:445:2:445:2 | d [post update] | taint.cpp:446:7:446:7 | d | |
| taint.cpp:445:2:445:2 | d [post update] | taint.cpp:447:7:447:7 | d | |
| taint.cpp:452:16:452:16 | a | taint.cpp:454:10:454:10 | a | |
| taint.cpp:452:24:452:24 | b | taint.cpp:455:6:455:6 | b | |
| taint.cpp:454:10:454:10 | a | taint.cpp:456:6:456:6 | c | |
| taint.cpp:455:6:455:6 | b | taint.cpp:452:16:452:16 | a | |
| taint.cpp:455:6:455:6 | b | taint.cpp:455:2:455:6 | ... = ... | |
| taint.cpp:456:6:456:6 | c | taint.cpp:452:24:452:24 | b | |
| taint.cpp:456:6:456:6 | c | taint.cpp:456:2:456:6 | ... = ... | |
| taint.cpp:462:6:462:11 | call to source | taint.cpp:462:2:462:13 | ... = ... | |
| taint.cpp:462:6:462:11 | call to source | taint.cpp:465:7:465:7 | x | |
| taint.cpp:462:6:462:11 | call to source | taint.cpp:468:7:468:7 | x | |
| taint.cpp:462:6:462:11 | call to source | taint.cpp:470:7:470:7 | x | |
| taint.cpp:463:6:463:6 | 0 | taint.cpp:463:2:463:6 | ... = ... | |
| taint.cpp:463:6:463:6 | 0 | taint.cpp:466:7:466:7 | y | |
| taint.cpp:463:6:463:6 | 0 | taint.cpp:468:10:468:10 | y | |
| taint.cpp:463:6:463:6 | 0 | taint.cpp:471:7:471:7 | y | |
| taint.cpp:468:7:468:7 | ref arg x | taint.cpp:470:7:470:7 | x | |
| taint.cpp:468:10:468:10 | ref arg y | taint.cpp:471:7:471:7 | y | |

View File

@@ -195,7 +195,7 @@ void test_memcpy(int *source) {
sink(x);
}
// --- swap ---
// --- std::swap ---
namespace std {
template<class T> constexpr void swap(T& a, T& b);
@@ -446,3 +446,27 @@ void test_qualifiers()
sink(d); // tainted
sink(d.getString()); // tainted
}
// --- non-standard swap ---
void swop(int &a, int &b)
{
int c = a;
a = b;
b = c;
}
void test_swop() {
int x, y;
x = source();
y = 0;
sink(x); // tainted
sink(y); // clean
swop(x, y);
sink(x); // clean [FALSE POSITIVE]
sink(y); // tainted
}

View File

@@ -58,3 +58,6 @@
| taint.cpp:439:10:439:18 | call to getMember | taint.cpp:437:15:437:20 | call to source |
| taint.cpp:446:7:446:7 | d | taint.cpp:445:14:445:28 | call to source |
| taint.cpp:447:9:447:17 | call to getString | taint.cpp:445:14:445:28 | call to source |
| taint.cpp:465:7:465:7 | x | taint.cpp:462:6:462:11 | call to source |
| taint.cpp:470:7:470:7 | x | taint.cpp:462:6:462:11 | call to source |
| taint.cpp:471:7:471:7 | y | taint.cpp:462:6:462:11 | call to source |

View File

@@ -42,3 +42,4 @@
| taint.cpp:439:10:439:18 | taint.cpp:437:15:437:20 | AST only |
| taint.cpp:446:7:446:7 | taint.cpp:445:14:445:28 | AST only |
| taint.cpp:447:9:447:17 | taint.cpp:445:14:445:28 | AST only |
| taint.cpp:471:7:471:7 | taint.cpp:462:6:462:11 | AST only |

View File

@@ -20,3 +20,5 @@
| taint.cpp:382:7:382:7 | a | taint.cpp:377:23:377:28 | source |
| taint.cpp:429:7:429:7 | b | taint.cpp:428:13:428:18 | call to source |
| taint.cpp:430:9:430:14 | member | taint.cpp:428:13:428:18 | call to source |
| taint.cpp:465:7:465:7 | x | taint.cpp:462:6:462:11 | call to source |
| taint.cpp:470:7:470:7 | x | taint.cpp:462:6:462:11 | call to source |

View File

@@ -8475,6 +8475,76 @@ ir.cpp:
# 1243| Type = [PointerType] const char *
# 1243| ValueCategory = prvalue(load)
# 1244| 3: [ReturnStmt] return ...
# 1248| [TopLevelFunction] char* strcpy(char*, char const*)
# 1248| params:
# 1248| 0: [Parameter] destination
# 1248| Type = [CharPointerType] char *
# 1248| 1: [Parameter] source
# 1248| Type = [PointerType] const char *
# 1249| [TopLevelFunction] char* strcat(char*, char const*)
# 1249| params:
# 1249| 0: [Parameter] destination
# 1249| Type = [CharPointerType] char *
# 1249| 1: [Parameter] source
# 1249| Type = [PointerType] const char *
# 1251| [TopLevelFunction] void test_strings(char*, char*)
# 1251| params:
# 1251| 0: [Parameter] s1
# 1251| Type = [CharPointerType] char *
# 1251| 1: [Parameter] s2
# 1251| Type = [CharPointerType] char *
# 1251| body: [Block] { ... }
# 1252| 0: [DeclStmt] declaration
# 1252| 0: [VariableDeclarationEntry] definition of buffer
# 1252| Type = [ArrayType] char[1024]
# 1252| init: [Initializer] initializer for buffer
# 1252| expr: [ArrayAggregateLiteral] {...}
# 1252| Type = [ArrayType] char[1024]
# 1252| ValueCategory = prvalue
# 1252| [0]: [CStyleCast] (char)...
# 1252| Conversion = [IntegralConversion] integral conversion
# 1252| Type = [PlainCharType] char
# 1252| Value = [CStyleCast] 0
# 1252| ValueCategory = prvalue
# 1252| expr: [Literal] 0
# 1252| Type = [IntType] int
# 1252| Value = [Literal] 0
# 1252| ValueCategory = prvalue
# 1254| 1: [ExprStmt] ExprStmt
# 1254| 0: [FunctionCall] call to strcpy
# 1254| Type = [CharPointerType] char *
# 1254| ValueCategory = prvalue
# 1254| 0: [ArrayToPointerConversion] array to pointer conversion
# 1254| Type = [CharPointerType] char *
# 1254| ValueCategory = prvalue
# 1254| expr: [VariableAccess] buffer
# 1254| Type = [ArrayType] char[1024]
# 1254| ValueCategory = lvalue
# 1254| 1: [CStyleCast] (const char *)...
# 1254| Conversion = [PointerConversion] pointer conversion
# 1254| Type = [PointerType] const char *
# 1254| ValueCategory = prvalue
# 1254| expr: [VariableAccess] s1
# 1254| Type = [CharPointerType] char *
# 1254| ValueCategory = prvalue(load)
# 1255| 2: [ExprStmt] ExprStmt
# 1255| 0: [FunctionCall] call to strcat
# 1255| Type = [CharPointerType] char *
# 1255| ValueCategory = prvalue
# 1255| 0: [ArrayToPointerConversion] array to pointer conversion
# 1255| Type = [CharPointerType] char *
# 1255| ValueCategory = prvalue
# 1255| expr: [VariableAccess] buffer
# 1255| Type = [ArrayType] char[1024]
# 1255| ValueCategory = lvalue
# 1255| 1: [CStyleCast] (const char *)...
# 1255| Conversion = [PointerConversion] pointer conversion
# 1255| Type = [PointerType] const char *
# 1255| ValueCategory = prvalue
# 1255| expr: [VariableAccess] s2
# 1255| Type = [CharPointerType] char *
# 1255| ValueCategory = prvalue(load)
# 1256| 3: [ReturnStmt] return ...
perf-regression.cpp:
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
# 4| params:

View File

@@ -1243,4 +1243,16 @@ void staticLocalWithConstructor(const char* dynamic) {
static String c(dynamic);
}
// --- strings ---
char *strcpy(char *destination, const char *source);
char *strcat(char *destination, const char *source);
void test_strings(char *s1, char *s2) {
char buffer[1024] = {0};
strcpy(buffer, s1);
strcat(buffer, s2);
}
// semmle-extractor-options: -std=c++17 --clang

View File

@@ -6432,6 +6432,57 @@ ir.cpp:
# 1241| mu1241_6(bool) = Store : &:r1241_1, r1241_5
#-----| Goto -> Block 1
# 1251| void test_strings(char*, char*)
# 1251| Block 0
# 1251| v1251_1(void) = EnterFunction :
# 1251| mu1251_2(unknown) = AliasedDefinition :
# 1251| mu1251_3(unknown) = InitializeNonLocal :
# 1251| mu1251_4(unknown) = UnmodeledDefinition :
# 1251| r1251_5(glval<char *>) = VariableAddress[s1] :
# 1251| mu1251_6(char *) = InitializeParameter[s1] : &:r1251_5
# 1251| r1251_7(char *) = Load : &:r1251_5, ~mu1251_6
# 1251| mu1251_8(unknown) = InitializeIndirection[s1] : &:r1251_7
# 1251| r1251_9(glval<char *>) = VariableAddress[s2] :
# 1251| mu1251_10(char *) = InitializeParameter[s2] : &:r1251_9
# 1251| r1251_11(char *) = Load : &:r1251_9, ~mu1251_10
# 1251| mu1251_12(unknown) = InitializeIndirection[s2] : &:r1251_11
# 1252| r1252_1(glval<char[1024]>) = VariableAddress[buffer] :
# 1252| mu1252_2(char[1024]) = Uninitialized[buffer] : &:r1252_1
# 1252| r1252_3(int) = Constant[0] :
# 1252| r1252_4(glval<char>) = PointerAdd[1] : r1252_1, r1252_3
# 1252| r1252_5(char) = Constant[0] :
# 1252| mu1252_6(char) = Store : &:r1252_4, r1252_5
# 1252| r1252_7(int) = Constant[1] :
# 1252| r1252_8(glval<char>) = PointerAdd[1] : r1252_1, r1252_7
# 1252| r1252_9(unknown[1023]) = Constant[0] :
# 1252| mu1252_10(unknown[1023]) = Store : &:r1252_8, r1252_9
# 1254| r1254_1(glval<unknown>) = FunctionAddress[strcpy] :
# 1254| r1254_2(glval<char[1024]>) = VariableAddress[buffer] :
# 1254| r1254_3(char *) = Convert : r1254_2
# 1254| r1254_4(glval<char *>) = VariableAddress[s1] :
# 1254| r1254_5(char *) = Load : &:r1254_4, ~mu1251_4
# 1254| r1254_6(char *) = Convert : r1254_5
# 1254| r1254_7(char *) = Call : func:r1254_1, 0:r1254_3, 1:r1254_6
# 1254| v1254_8(void) = ^BufferReadSideEffect[1] : &:r1254_6, ~mu1251_4
# 1254| mu1254_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r1254_3
# 1255| r1255_1(glval<unknown>) = FunctionAddress[strcat] :
# 1255| r1255_2(glval<char[1024]>) = VariableAddress[buffer] :
# 1255| r1255_3(char *) = Convert : r1255_2
# 1255| r1255_4(glval<char *>) = VariableAddress[s2] :
# 1255| r1255_5(char *) = Load : &:r1255_4, ~mu1251_4
# 1255| r1255_6(char *) = Convert : r1255_5
# 1255| r1255_7(char *) = Call : func:r1255_1, 0:r1255_3, 1:r1255_6
# 1255| v1255_8(void) = ^BufferReadSideEffect[0] : &:r1255_3, ~mu1251_4
# 1255| v1255_9(void) = ^BufferReadSideEffect[1] : &:r1255_6, ~mu1251_4
# 1255| mu1255_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1255_3
# 1256| v1256_1(void) = NoOp :
# 1251| v1251_13(void) = ReturnIndirection : &:r1251_7, ~mu1251_4
# 1251| v1251_14(void) = ReturnIndirection : &:r1251_11, ~mu1251_4
# 1251| v1251_15(void) = ReturnVoid :
# 1251| v1251_16(void) = UnmodeledUse : mu*
# 1251| v1251_17(void) = AliasedUse : ~mu1251_4
# 1251| v1251_18(void) = ExitFunction :
perf-regression.cpp:
# 6| void Big::Big()
# 6| Block 0

View File

@@ -4,9 +4,9 @@
| test.c:25:9:25:14 | ExprStmt | Function f4 should return a value of type int but does not return a value here |
| test.c:39:9:39:14 | ExprStmt | Function f6 should return a value of type int but does not return a value here |
| test.cpp:16:1:18:1 | { ... } | Function g2 should return a value of type MyValue but does not return a value here |
| test.cpp:48:2:48:26 | if (...) ... | Function g7 should return a value of type MyValue but does not return a value here |
| test.cpp:52:1:52:1 | return ... | Function g7 should return a value of type MyValue but does not return a value here |
| test.cpp:74:1:76:1 | { ... } | Function g10 should return a value of type second but does not return a value here |
| test.cpp:86:1:88:1 | { ... } | Function g12 should return a value of type second but does not return a value here |
| test.cpp:108:2:111:2 | if (...) ... | Function g14 should return a value of type int but does not return a value here |
| test.cpp:112:1:112:1 | return ... | Function g14 should return a value of type int but does not return a value here |
| test.cpp:134:2:134:36 | ExprStmt | Function g16 should return a value of type int but does not return a value here |
| test.cpp:141:3:141:37 | ExprStmt | Function g17 should return a value of type int but does not return a value here |

View File

@@ -48,7 +48,7 @@ MyValue g7(bool c)
if (c) return MyValue(7);
DONOTHING
DONOTHING
// BAD [the alert here is unfortunately placed]
// BAD
}
typedef void MYVOID;

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -31,7 +31,7 @@ This table lists `Declaration <https://help.semmle.com/qldoc/cpp/semmle/code/cpp
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``namespace`` *N* ``{`` ... ``float`` *var* ``;`` ... ``}`` | `NamespaceVariable <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/Variable.qll/type.Variable$NamespaceVariable.html>`__ | |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``int`` *func* ``( void ) {`` ... ``float`` *var* ``;`` ... ``}`` | `LocalVariable <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/Variable.qll/type.Variable$LocalVariable.html>`__ | |
| ``int`` *func* ``( void ) {`` ... ``float`` *var* ``;`` ... ``}`` | `LocalVariable <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/Variable.qll/type.Variable$LocalVariable.html>`__ | See also `Initializer <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/Initializer.qll/type.Initializer$Initializer.html>`__ |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``class`` *C* ``{`` ... ``int`` *var* ``;`` ... ``}`` | `MemberVariable <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/Variable.qll/type.Variable$MemberVariable.html>`__ | |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
@@ -286,6 +286,8 @@ This table lists subclasses of `Expr <https://help.semmle.com/qldoc/cpp/semmle/c
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ``noexcept (`` `Expr <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/exprs/Expr.qll/type.Expr$Expr.html>`__ ``)`` | `NoExceptExpr <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/exprs/Expr.qll/type.Expr$NoExceptExpr.html>`__ | |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| `Expr <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/exprs/Expr.qll/type.Expr$Expr.html>`__ ``=`` `Expr <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/exprs/Expr.qll/type.Expr$Expr.html>`__ | `AssignExpr <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/exprs/Assignment.qll/type.Assignment$AssignExpr.html>`__ | See also `Initializer <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/Initializer.qll/type.Initializer$Initializer.html>`__ |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| `Expr <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/exprs/Expr.qll/type.Expr$Expr.html>`__ ``+=`` `Expr <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/exprs/Expr.qll/type.Expr$Expr.html>`__ | | `AssignAddExpr <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/exprs/Assignment.qll/type.Assignment$AssignAddExpr.html>`__ | |
| | | `AssignPointerAddExpr <https://help.semmle.com/qldoc/cpp/semmle/code/cpp/exprs/Assignment.qll/type.Assignment$AssignPointerAddExpr.html>`__ | |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

View File

@@ -296,6 +296,22 @@ The following aggregates are available in QL:
evaluates to the empty set (instead of defaulting to ``0`` or the empty string).
This is useful if you're only interested in results where the aggregation body is non-trivial.
.. index:: unique
- ``unique``: This aggregate depends on the values of ``<expression>`` over all possible assignments to
the aggregation variables. If there is a unique value of ``<expression>`` over the aggregation variables,
then the aggregate evaluates to that value.
Otherwise, the aggregate has no value.
For example, the following query returns the positive integers ``1``, ``2``, ``3``, ``4``, ``5``.
For negative integers ``x``, the expressions ``x`` and ``x.abs()`` have different values, so the
value for ``y`` in the aggregate expression is not uniquely determined. ::
from int x
where x in [-5 .. 5] and x != 0
select unique(int y | y = x or y = x.abs() | y)
The ``unique`` aggregate is supported from release 2.1.0 of the CodeQL CLI, and release 1.24 of LGTM Enterprise.
Evaluation of aggregates
========================

View File

@@ -1058,6 +1058,7 @@ An aggregation can be written in one of two forms:
aggregation ::= aggid ("[" expr "]")? "(" (var_decls)? ("|" (formula)? ("|" as_exprs ("order" "by" aggorderbys)?)?)? ")"
| aggid ("[" expr "]")? "(" as_exprs ("order" "by" aggorderbys)? ")"
| "unique" "(" var_decls "|" (formula)? ("|" as_exprs)? ")"
aggid ::= "avg" | "concat" | "count" | "max" | "min" | "rank" | "strictconcat" | "strictcount" | "strictsum" | "sum"
@@ -1098,7 +1099,7 @@ The typing environment for ordering directives is obtained by taking the typing
The number and types of the aggregation expressions are restricted as follows:
- A ``max``, ``min`` or ``rank`` aggregation must have a single expression.
- A ``max``, ``min``, ``rank`` or ``unique`` aggregation must have a single expression.
- The type of the expression in a ``max``, ``min`` or ``rank`` aggregation without an ordering directive expression must be an orderable type.
- A ``count`` or ``strictcount`` aggregation must not have an expression.
- A ``sum``, ``strictsum`` or ``avg`` aggregation must have a single aggregation expression, which must have a type which is a subtype of ``float``.
@@ -1140,6 +1141,8 @@ The values of the aggregation expression are given by applying the aggregation f
- If the aggregation id is ``strictconcat``, then the result is the same as for ``concat`` except in the case where there are no aggregation tuples in which case the aggregation has no value.
- If the aggregation id is ``unique``, then the result is the value of the aggregation variable if there is precisely one such value. Otherwise, the aggregation has no value.
Any
~~~
@@ -1976,7 +1979,8 @@ The complete grammar for QL is as follows:
aggregation ::= aggid ("[" expr "]")? "(" (var_decls)? ("|" (formula)? ("|" as_exprs ("order" "by" aggorderbys)?)?)? ")"
| aggid ("[" expr "]")? "(" as_exprs ("order" "by" aggorderbys)? ")"
| "unique" "(" var_decls "|" (formula)? ("|" as_exprs)? ")"
aggid ::= "avg" | "concat" | "count" | "max" | "min" | "rank" | "strictconcat" | "strictcount" | "strictsum" | "sum"
aggorderbys ::= aggorderby ("," aggorderby)*

View File

@@ -0,0 +1,8 @@
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// BAD: a URL from a remote source is opened with URL#openStream()
URL url = new URL(request.getParameter("url"));
InputStream inputStream = new URL(url).openStream();
}
}

View File

@@ -0,0 +1,33 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Calling <code>openStream</code> on URLs created from remote source can lead to local file disclosure.</p>
<p>If <code>openStream</code> is called on a <code>java.net.URL</code>, that was created from a remote source,
an attacker can try to pass absolute URLs starting with <code>file://</code> or <code>jar://</code> to access
local resources in addition to remote ones.</p>
</overview>
<recommendation>
<p>When you construct a URL using <code>java.net.URL</code> from a remote source,
don't call <code>openStream</code> on it. Instead, use an HTTP Client to fetch the URL and access its content.
You should also validate the URL to check that it uses the correct protocol and host combination.</p>
</recommendation>
<example>
<p>The following example shows an URL that is constructed from a request parameter. Afterwards <code>openStream</code>
is called on the URL, potentially leading to a local file access.</p>
<sample src="OpenStream.java" />
</example>
<references>
<li>Java Platform, Standard Edition 11, API Specification:
<a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/URL.html">
Class URL</a>.
</li>
<!-- LocalWords: CWE -->
</references>
</qhelp>

View File

@@ -0,0 +1,55 @@
/**
* @name openStream called on URLs created from remote source
* @description Calling openStream on URLs created from remote source
* can lead to local file disclosure.
* @kind path-problem
*/
import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources
import DataFlow::PathGraph
class URLConstructor extends ClassInstanceExpr {
URLConstructor() { this.getConstructor().getDeclaringType() instanceof TypeUrl }
Expr stringArg() {
// Query only in URL's that were constructed by calling the single parameter string constructor.
this.getConstructor().getNumberOfParameters() = 1 and
this.getConstructor().getParameter(0).getType() instanceof TypeString and
result = this.getArgument(0)
}
}
class URLOpenStreamMethod extends Method {
URLOpenStreamMethod() {
this.getDeclaringType() instanceof TypeUrl and
this.getName() = "openStream"
}
}
class RemoteURLToOpenStreamFlowConfig extends TaintTracking::Configuration {
RemoteURLToOpenStreamFlowConfig() { this = "OpenStream::RemoteURLToOpenStreamFlowConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess m |
sink.asExpr() = m.getQualifier() and m.getMethod() instanceof URLOpenStreamMethod
)
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(URLConstructor u |
node1.asExpr() = u.stringArg() and
node2.asExpr() = u
)
}
}
from DataFlow::PathNode source, DataFlow::PathNode sink, MethodAccess call
where
sink.getNode().asExpr() = call.getQualifier() and
any(RemoteURLToOpenStreamFlowConfig c).hasFlowPath(source, sink)
select call, source, sink,
"URL on which openStream is called may have been constructed from remote source"

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -2089,6 +2089,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
int getParameterPos() { p.isParameterOf(_, result) }
override string toString() { result = p + ": " + ap }
predicate hasLocationInfo(
@@ -2482,13 +2484,15 @@ pragma[nomagic]
private predicate paramFlowsThrough(
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, Configuration config
) {
exists(PathNodeMid mid, ReturnNodeExt ret |
exists(PathNodeMid mid, ReturnNodeExt ret, int pos |
mid.getNode() = ret and
kind = ret.getKind() and
cc = mid.getCallContext() and
sc = mid.getSummaryCtx() and
config = mid.getConfiguration() and
ap = mid.getAp()
ap = mid.getAp() and
pos = sc.getParameterPos() and
not kind.(ParamUpdateReturnKind).getPosition() = pos
)
}

View File

@@ -12,6 +12,10 @@ class TypeSocket extends RefType {
TypeSocket() { hasQualifiedName("java.net", "Socket") }
}
class TypeUrl extends RefType {
TypeUrl() { hasQualifiedName("java.net", "URL") }
}
class URLConnectionGetInputStreamMethod extends Method {
URLConnectionGetInputStreamMethod() {
getDeclaringType() instanceof TypeUrlConnection and

View File

@@ -482,6 +482,7 @@ public class ESNextParser extends JSXParser {
if (code == '_') {
if (underscoreAllowed) {
seenUnderscoreNumericSeparator = true;
// no adjacent underscores
underscoreAllowed = false;
++this.pos;

View File

@@ -145,6 +145,11 @@ public class Parser {
private Stack<LabelInfo> labels;
protected int yieldPos, awaitPos;
/**
* Set to true by {@link ESNextParser#readInt} if the parsed integer contains an underscore.
*/
protected boolean seenUnderscoreNumericSeparator = false;
/**
* For readability purposes, we pass this instead of false as the argument to the
* hasDeclareKeyword parameter (which only exists in TypeScript).
@@ -654,7 +659,7 @@ public class Parser {
case 58:
++this.pos;
return this.finishToken(TokenType.colon);
case 35:
case 35:
++this.pos;
return this.finishToken(TokenType.pound);
case 63:
@@ -847,6 +852,10 @@ public class Parser {
}
String str = inputSubstring(start, this.pos);
if (seenUnderscoreNumericSeparator) {
str = str.replace("_", "");
seenUnderscoreNumericSeparator = false;
}
Number val = null;
if (isFloat) val = parseFloat(str);
else if (!octal || str.length() == 1) val = parseInt(str, 10);

View File

@@ -565,17 +565,40 @@ public class AutoBuild {
extractTypeScript(
defaultExtractor, filesToExtract, tsconfigFiles, dependencyInstallationResult);
boolean hasTypeScriptFiles = extractedFiles.size() > 0;
// extract remaining files
for (Path f : filesToExtract) {
if (extractedFiles.add(f)) {
FileExtractor extractor = defaultExtractor;
if (!fileTypes.isEmpty()) {
String extension = FileUtil.extension(f);
if (customExtractors.containsKey(extension)) extractor = customExtractors.get(extension);
}
extract(extractor, f, null);
if (extractedFiles.contains(f))
continue;
if (hasTypeScriptFiles && isFileDerivedFromTypeScriptFile(f, extractedFiles)) {
continue;
}
extractedFiles.add(f);
FileExtractor extractor = defaultExtractor;
if (!fileTypes.isEmpty()) {
String extension = FileUtil.extension(f);
if (customExtractors.containsKey(extension)) extractor = customExtractors.get(extension);
}
extract(extractor, f, null);
}
}
/**
* Returns true if the given path is likely the output of compiling a TypeScript file
* which we have already extracted.
*/
private boolean isFileDerivedFromTypeScriptFile(Path path, Set<Path> extractedFiles) {
String name = path.getFileName().toString();
if (!name.endsWith(".js"))
return false;
String stem = name.substring(0, name.length() - ".js".length());
for (String ext : FileType.TYPESCRIPT.getExtensions()) {
if (extractedFiles.contains(path.getParent().resolve(stem + ext))) {
return true;
}
}
return false;
}
/** Returns true if yarn is installed, otherwise prints a warning and returns false. */
@@ -613,7 +636,7 @@ public class AutoBuild {
}
return null;
}
/**
* Returns an existing file named <code>dir/stem.ext</code> where <code>ext</code> is any TypeScript or JavaScript extension,
* or <code>null</code> if no such file exists.
@@ -623,7 +646,7 @@ public class AutoBuild {
if (resolved != null) return resolved;
return tryResolveWithExtensions(dir, stem, FileType.JS.getExtensions());
}
/**
* Gets a child of a JSON object as a string, or <code>null</code>.
*/
@@ -658,7 +681,7 @@ public class AutoBuild {
if (!verifyYarnInstallation()) {
return DependencyInstallationResult.empty;
}
final Path sourceRoot = Paths.get(".").toAbsolutePath();
final Path virtualSourceRoot = Paths.get(EnvironmentVariables.getScratchDir()).toAbsolutePath();
@@ -746,7 +769,7 @@ public class AutoBuild {
for (Path file : packageJsonFiles.keySet()) {
Path relativePath = sourceRoot.relativize(file);
Path virtualFile = virtualSourceRoot.resolve(relativePath);
try {
Files.createDirectories(virtualFile.getParent());
try (Writer writer = Files.newBufferedWriter(virtualFile)) {
@@ -794,7 +817,7 @@ public class AutoBuild {
*/
private Path guessPackageMainFile(Path packageJsonFile, JsonObject packageJson, Iterable<String> extensions) {
Path packageDir = packageJsonFile.getParent();
// Try <package_dir>/index.ts.
Path resolved = tryResolveWithExtensions(packageDir, "index", extensions);
if (resolved != null) {

View File

@@ -1,5 +1,13 @@
package com.semmle.js.extractor;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import com.semmle.js.extractor.ExtractorConfig.HTMLHandling;
import com.semmle.js.extractor.ExtractorConfig.Platform;
import com.semmle.js.extractor.ExtractorConfig.SourceType;
@@ -23,13 +31,6 @@ import com.semmle.util.language.LegacyLanguage;
import com.semmle.util.process.ArgsParser;
import com.semmle.util.process.ArgsParser.FileMode;
import com.semmle.util.trap.TrapWriter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
/** The main entry point of the JavaScript extractor. */
public class Main {
@@ -37,7 +38,7 @@ public class Main {
* A version identifier that should be updated every time the extractor changes in such a way that
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
*/
public static final String EXTRACTOR_VERSION = "2020-02-14";
public static final String EXTRACTOR_VERSION = "2020-04-01";
public static final Pattern NEWLINE = Pattern.compile("\n");
@@ -190,10 +191,29 @@ public class Main {
// Extract files that were not part of a project.
for (File f : files) {
if (isFileDerivedFromTypeScriptFile(f))
continue;
ensureFileIsExtracted(f, ap);
}
}
/**
* Returns true if the given path is likely the output of compiling a TypeScript file
* which we have already extracted.
*/
private boolean isFileDerivedFromTypeScriptFile(File path) {
String name = path.getName();
if (!name.endsWith(".js"))
return false;
String stem = name.substring(0, name.length() - ".js".length());
for (String ext : FileType.TYPESCRIPT.getExtensions()) {
if (new File(path.getParent(), stem + ext).exists()) {
return true;
}
}
return false;
}
private void extractTypeTable(File fileHandle, TypeTable table) {
TrapWriter trapWriter =
extractorOutputConfig

View File

@@ -262,6 +262,17 @@ abstract class Configuration extends string {
predicate isAdditionalLoadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
none()
}
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
*/
predicate isAdditionalLoadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
none()
}
}
/**
@@ -548,6 +559,19 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
*/
cached
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* EXPERIMENTAL. This API may change in the future.
*
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
*/
cached
predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
loadProp = storeProp and
loadStoreStep(pred, succ, loadProp)
}
}
/**
@@ -666,7 +690,7 @@ private predicate exploratoryFlowStep(
basicLoadStep(pred, succ, _) or
isAdditionalStoreStep(pred, succ, _, cfg) or
isAdditionalLoadStep(pred, succ, _, cfg) or
isAdditionalLoadStoreStep(pred, succ, _, cfg) or
isAdditionalLoadStoreStep(pred, succ, _, _, cfg) or
// the following three disjuncts taken together over-approximate flow through
// higher-order calls
callback(pred, succ) or
@@ -910,14 +934,22 @@ private predicate isAdditionalStoreStep(
}
/**
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
*/
private predicate isAdditionalLoadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string prop, DataFlow::Configuration cfg
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp,
DataFlow::Configuration cfg
) {
any(AdditionalFlowStep s).loadStoreStep(pred, succ, prop)
any(AdditionalFlowStep s).loadStoreStep(pred, succ, loadProp, storeProp)
or
cfg.isAdditionalLoadStoreStep(pred, succ, prop)
cfg.isAdditionalLoadStoreStep(pred, succ, loadProp, storeProp)
or
loadProp = storeProp and
(
any(AdditionalFlowStep s).loadStoreStep(pred, succ, loadProp)
or
cfg.isAdditionalLoadStoreStep(pred, succ, loadProp)
)
}
/**
@@ -963,7 +995,7 @@ private predicate reachableFromStoreBase(
s2.getEndLabel())
)
or
exists(PathSummary oldSummary, PathSummary newSummary |
exists(PathSummary newSummary, PathSummary oldSummary |
reachableFromStoreBaseStep(prop, rhs, nd, cfg, oldSummary, newSummary) and
summary = oldSummary.appendValuePreserving(newSummary)
)
@@ -980,11 +1012,15 @@ private predicate reachableFromStoreBaseStep(
string prop, DataFlow::Node rhs, DataFlow::Node nd, DataFlow::Configuration cfg,
PathSummary oldSummary, PathSummary newSummary
) {
exists(DataFlow::Node mid | reachableFromStoreBase(prop, rhs, mid, cfg, oldSummary) |
exists(DataFlow::Node mid |
reachableFromStoreBase(prop, rhs, mid, cfg, oldSummary) and
flowStep(mid, cfg, nd, newSummary)
or
isAdditionalLoadStoreStep(mid, nd, prop, cfg) and
newSummary = PathSummary::level()
exists(string midProp |
reachableFromStoreBase(midProp, rhs, mid, cfg, oldSummary) and
isAdditionalLoadStoreStep(mid, nd, midProp, prop, cfg) and
newSummary = PathSummary::level()
)
)
}

View File

@@ -287,32 +287,32 @@ module TaintTracking {
/**
* A taint propagating data flow edge for assignments of the form `o[k] = v`, where
* `k` is not a constant and `o` refers to some object literal; in this case, we consider
* taint to flow from `v` to any variable that refers to the object literal.
* taint to flow from `v` to that object literal.
*
* The rationale for this heuristic is that if properties of `o` are accessed by
* computed (that is, non-constant) names, then `o` is most likely being treated as
* a map, not as a real object. In this case, it makes sense to consider the entire
* map to be tainted as soon as one of its entries is.
*/
private class DictionaryTaintStep extends AdditionalTaintStep, DataFlow::ValueNode {
override VarAccess astNode;
DataFlow::Node source;
DictionaryTaintStep() {
exists(AssignExpr assgn, IndexExpr idx, AbstractObjectLiteral obj |
assgn.getTarget() = idx and
idx.getBase().analyze().getAValue() = obj and
not exists(idx.getPropertyName()) and
astNode.analyze().getAValue() = obj and
source = DataFlow::valueNode(assgn.getRhs())
)
}
private class DictionaryTaintStep extends AdditionalTaintStep {
DictionaryTaintStep() { dictionaryTaintStep(_, this) }
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = source and succ = this
succ = this and
dictionaryTaintStep(pred, succ)
}
}
/** Holds if there is a step `pred -> succ` used by `DictionaryTaintStep`. */
private predicate dictionaryTaintStep(DataFlow::Node pred, DataFlow::ObjectLiteralNode succ) {
exists(AssignExpr assgn, IndexExpr idx |
assgn.getTarget() = idx and
succ.flowsToExpr(idx.getBase()) and
not exists(idx.getPropertyName()) and
pred = DataFlow::valueNode(assgn.getRhs())
)
}
/**
* A taint propagating data flow edge for assignments of the form `c1.state.p = v`,
* where `c1` is an instance of React component `C`; in this case, we consider
@@ -558,8 +558,8 @@ module TaintTracking {
}
/**
* Holds if `params` is a `URLSearchParams` object providing access to
* the parameters encoded in `input`.
* Holds if `params` is a construction of a `URLSearchParams` that parses
* the parameters in `input`.
*/
predicate isUrlSearchParams(DataFlow::SourceNode params, DataFlow::Node input) {
exists(DataFlow::GlobalVarRefNode urlSearchParams, NewExpr newUrlSearchParams |
@@ -568,34 +568,92 @@ module TaintTracking {
params.asExpr() = newUrlSearchParams and
input.asExpr() = newUrlSearchParams.getArgument(0)
)
or
exists(DataFlow::NewNode newUrl |
newUrl = DataFlow::globalVarRef("URL").getAnInstantiation() and
params = newUrl.getAPropertyRead("searchParams") and
input = newUrl.getArgument(0)
)
}
/**
* A pseudo-property a `URL` that stores a value that can be obtained
* with a `get` or `getAll` call to the `searchParams` property.
*/
private string hiddenUrlPseudoProperty() { result = "$hiddenSearchPararms" }
/**
* A pseudo-property on a `URLSearchParams` that can be obtained
* with a `get` or `getAll` call.
*/
private string getableUrlPseudoProperty() { result = "$gettableSearchPararms" }
/**
* A taint propagating data flow edge arising from URL parameter parsing.
*/
private class UrlSearchParamsTaintStep extends AdditionalTaintStep, DataFlow::ValueNode {
DataFlow::Node source;
private class UrlSearchParamsTaintStep extends DataFlow::AdditionalFlowStep, DataFlow::ValueNode {
/**
* Holds if `succ` is a `URLSearchParams` providing access to the
* parameters encoded in `pred`.
*/
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
isUrlSearchParams(succ, pred) and succ = this
}
UrlSearchParamsTaintStep() {
// either this is itself an `URLSearchParams` object
isUrlSearchParams(this, source)
or
// or this is a call to `get` or `getAll` on a `URLSearchParams` object
exists(DataFlow::SourceNode searchParams, string m |
isUrlSearchParams(searchParams, source) and
this = searchParams.getAMethodCall(m) and
m.matches("get%")
/**
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
*
* This step is used to model 3 facts:
* 1) A `URL` constructed using `url = new URL(input)` transfers taint from `input` to `url.searchParams`, `url.hash`, and `url.search`.
* 2) Accessing the `searchParams` on a `URL` results in a `URLSearchParams` object (See the loadStoreStep method on this class and hiddenUrlPseudoProperty())
* 3) A `URLSearchParams` object (either `url.searchParams` or `new URLSearchParams(input)`) has a tainted value,
* which can be accessed using a `get` or `getAll` call. (See getableUrlPseudoProperty())
*/
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
succ = this and
(
(
prop = "searchParams" or
prop = "hash" or
prop = "search" or
prop = hiddenUrlPseudoProperty()
) and
exists(DataFlow::NewNode newUrl | succ = newUrl |
newUrl = DataFlow::globalVarRef("URL").getAnInstantiation() and
pred = newUrl.getArgument(0)
)
or
prop = getableUrlPseudoProperty() and
isUrlSearchParams(succ, pred)
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = source and succ = this
/**
* Holds if the property `loadStep` should be copied from the object `pred` to the property `storeStep` of object `succ`.
*
* This step is used to copy the value of our pseudo-property that can later be accessed using a `get` or `getAll` call.
* For an expression `url.searchParams`, the property `hiddenUrlPseudoProperty()` from the `url` object is stored in the property `getableUrlPseudoProperty()` on `url.searchParams`.
*/
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
succ = this and
loadProp = hiddenUrlPseudoProperty() and
storeProp = getableUrlPseudoProperty() and
exists(DataFlow::PropRead read | read = succ |
read.getPropertyName() = "searchParams" and
read.getBase() = pred
)
}
/**
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*
* This step is used to load the value stored in the pseudo-property `getableUrlPseudoProperty()`.
*/
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
succ = this and
prop = getableUrlPseudoProperty() and
// this is a call to `get` or `getAll` on a `URLSearchParams` object
exists(string m, DataFlow::MethodCallNode call | call = succ |
call.getMethodName() = m and
call.getReceiver() = pred and
m.matches("get%")
)
}
}

View File

@@ -23,5 +23,31 @@ module DomBasedXss {
or
node instanceof Sanitizer
}
override predicate isAdditionalLoadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string predProp, string succProp
) {
exists(DataFlow::PropRead read |
pred = read.getBase() and
succ = read and
read.getPropertyName() = "hash" and
predProp = "hash" and
succProp = urlSuffixPseudoProperty()
)
}
override predicate isAdditionalLoadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
exists(DataFlow::MethodCallNode call, string name |
name = "substr" or name = "substring" or name = "slice"
|
call.getMethodName() = name and
not call.getArgument(0).getIntValue() = 0 and
pred = call.getReceiver() and
succ = call and
prop = urlSuffixPseudoProperty()
)
}
}
private string urlSuffixPseudoProperty() { result = "$UrlSuffix$" }
}

View File

@@ -1,3 +1,125 @@
getIntValue
| tst2.ts:1:21:1:21 | 1 | 1 |
| tst.js:6:1:6:1 | 1 | 1 |
| tst.js:11:1:11:2 | -1 | -1 |
| tst.js:11:2:11:2 | 1 | 1 |
| tst.js:12:2:12:2 | 0 | 0 |
| tst.js:26:3:26:3 | 0 | 0 |
| tst.js:29:6:29:6 | 0 | 0 |
| tst.js:35:1:35:1 | 1 | 1 |
| tst.js:35:5:35:5 | 2 | 2 |
| tst.js:35:9:35:9 | 3 | 3 |
| tst.js:37:1:37:3 | (1) | 1 |
| tst.js:37:2:37:2 | 1 | 1 |
| tst.js:39:4:39:4 | 1 | 1 |
| tst.js:40:1:40:1 | 1 | 1 |
| tst.js:42:1:42:1 | 1 | 1 |
| tst.js:42:4:42:4 | 2 | 2 |
| tst.js:42:7:42:7 | 3 | 3 |
| tst.js:43:4:43:4 | 2 | 2 |
| tst.js:43:7:43:7 | 3 | 3 |
| tst.js:44:1:44:1 | 1 | 1 |
| tst.js:44:7:44:7 | 3 | 3 |
| tst.js:45:1:45:1 | 1 | 1 |
| tst.js:45:4:45:4 | 2 | 2 |
| tst.js:47:5:47:5 | 1 | 1 |
| tst.js:48:7:48:7 | 1 | 1 |
| tst.js:49:6:49:6 | 1 | 1 |
| tst.js:52:5:52:9 | 1_000 | 1000 |
| tst.js:53:5:53:13 | 1_000_123 | 1000123 |
| tst.js:54:5:54:17 | 1_000_000_000 | 1000000000 |
| tst.js:56:5:56:10 | 123_00 | 12300 |
| tst.js:57:5:57:10 | 12_300 | 12300 |
| tst.js:58:5:58:12 | 12345_00 | 1234500 |
| tst.js:59:5:59:12 | 123_4500 | 1234500 |
| tst.js:60:5:60:13 | 1_234_500 | 1234500 |
| tst.js:62:5:62:14 | 0xaa_bb_cc | 11189196 |
getFloatValue
| tst2.ts:1:21:1:21 | 1 | 1.0 |
| tst.js:6:1:6:1 | 1 | 1.0 |
| tst.js:11:2:11:2 | 1 | 1.0 |
| tst.js:12:2:12:2 | 0 | 0.0 |
| tst.js:26:3:26:3 | 0 | 0.0 |
| tst.js:29:6:29:6 | 0 | 0.0 |
| tst.js:35:1:35:1 | 1 | 1.0 |
| tst.js:35:5:35:5 | 2 | 2.0 |
| tst.js:35:9:35:9 | 3 | 3.0 |
| tst.js:37:2:37:2 | 1 | 1.0 |
| tst.js:39:4:39:4 | 1 | 1.0 |
| tst.js:40:1:40:1 | 1 | 1.0 |
| tst.js:42:1:42:1 | 1 | 1.0 |
| tst.js:42:4:42:4 | 2 | 2.0 |
| tst.js:42:7:42:7 | 3 | 3.0 |
| tst.js:43:4:43:4 | 2 | 2.0 |
| tst.js:43:7:43:7 | 3 | 3.0 |
| tst.js:44:1:44:1 | 1 | 1.0 |
| tst.js:44:7:44:7 | 3 | 3.0 |
| tst.js:45:1:45:1 | 1 | 1.0 |
| tst.js:45:4:45:4 | 2 | 2.0 |
| tst.js:47:5:47:5 | 1 | 1.0 |
| tst.js:48:7:48:7 | 1 | 1.0 |
| tst.js:49:6:49:6 | 1 | 1.0 |
| tst.js:52:5:52:9 | 1_000 | 1000.0 |
| tst.js:53:5:53:13 | 1_000_123 | 1000123.0 |
| tst.js:54:5:54:17 | 1_000_000_000 | 1.0E9 |
| tst.js:55:5:55:18 | 101_475_938.38 | 1.0147593838E8 |
| tst.js:56:5:56:10 | 123_00 | 12300.0 |
| tst.js:57:5:57:10 | 12_300 | 12300.0 |
| tst.js:58:5:58:12 | 12345_00 | 1234500.0 |
| tst.js:59:5:59:12 | 123_4500 | 1234500.0 |
| tst.js:60:5:60:13 | 1_234_500 | 1234500.0 |
| tst.js:61:5:61:10 | 1e1_00 | 1.0E100 |
| tst.js:62:5:62:14 | 0xaa_bb_cc | 1.1189196E7 |
getLiteralValue
| tst2.ts:1:21:1:21 | 1 | 1 |
| tst.js:1:1:1:3 | "a" | a |
| tst.js:2:1:2:3 | "b" | b |
| tst.js:2:7:2:9 | "c" | c |
| tst.js:3:1:3:3 | "d" | d |
| tst.js:3:7:3:9 | "e" | e |
| tst.js:3:13:3:15 | "f" | f |
| tst.js:6:1:6:1 | 1 | 1 |
| tst.js:8:1:8:5 | false | false |
| tst.js:9:1:9:4 | true | true |
| tst.js:11:2:11:2 | 1 | 1 |
| tst.js:12:2:12:2 | 0 | 0 |
| tst.js:14:1:14:4 | null | null |
| tst.js:20:1:20:3 | /x/ | /x/ |
| tst.js:24:5:24:7 | "x" | x |
| tst.js:26:3:26:3 | 0 | 0 |
| tst.js:29:6:29:6 | 0 | 0 |
| tst.js:35:1:35:1 | 1 | 1 |
| tst.js:35:5:35:5 | 2 | 2 |
| tst.js:35:9:35:9 | 3 | 3 |
| tst.js:37:2:37:2 | 1 | 1 |
| tst.js:39:4:39:4 | 1 | 1 |
| tst.js:40:1:40:1 | 1 | 1 |
| tst.js:42:1:42:1 | 1 | 1 |
| tst.js:42:4:42:4 | 2 | 2 |
| tst.js:42:7:42:7 | 3 | 3 |
| tst.js:43:4:43:4 | 2 | 2 |
| tst.js:43:7:43:7 | 3 | 3 |
| tst.js:44:1:44:1 | 1 | 1 |
| tst.js:44:7:44:7 | 3 | 3 |
| tst.js:45:1:45:1 | 1 | 1 |
| tst.js:45:4:45:4 | 2 | 2 |
| tst.js:47:5:47:5 | 1 | 1 |
| tst.js:48:7:48:7 | 1 | 1 |
| tst.js:49:6:49:6 | 1 | 1 |
| tst.js:52:5:52:9 | 1_000 | 1000 |
| tst.js:53:5:53:13 | 1_000_123 | 1000123 |
| tst.js:54:5:54:17 | 1_000_000_000 | 1000000000 |
| tst.js:55:5:55:18 | 101_475_938.38 | 1.0147593838E8 |
| tst.js:56:5:56:10 | 123_00 | 12300 |
| tst.js:57:5:57:10 | 12_300 | 12300 |
| tst.js:58:5:58:12 | 12345_00 | 1234500 |
| tst.js:59:5:59:12 | 123_4500 | 1234500 |
| tst.js:60:5:60:13 | 1_234_500 | 1234500 |
| tst.js:61:5:61:10 | 1e1_00 | 1.0E100 |
| tst.js:62:5:62:14 | 0xaa_bb_cc | 11189196 |
#select
| tst2.ts:1:13:1:21 | <number>1 |
| tst2.ts:1:21:1:21 | 1 |
| tst.js:1:1:1:3 | "a" |
| tst.js:2:1:2:3 | "b" |
| tst.js:2:1:2:9 | "b" + "c" |
@@ -59,5 +181,25 @@
| tst.js:48:1:48:7 | x.p = 1 |
| tst.js:48:7:48:7 | 1 |
| tst.js:49:6:49:6 | 1 |
| tst.ts:1:13:1:21 | <number>1 |
| tst.ts:1:21:1:21 | 1 |
| tst.js:52:1:52:9 | x = 1_000 |
| tst.js:52:5:52:9 | 1_000 |
| tst.js:53:1:53:13 | x = 1_000_123 |
| tst.js:53:5:53:13 | 1_000_123 |
| tst.js:54:1:54:17 | x = 1_000_000_000 |
| tst.js:54:5:54:17 | 1_000_000_000 |
| tst.js:55:1:55:18 | x = 101_475_938.38 |
| tst.js:55:5:55:18 | 101_475_938.38 |
| tst.js:56:1:56:10 | x = 123_00 |
| tst.js:56:5:56:10 | 123_00 |
| tst.js:57:1:57:10 | x = 12_300 |
| tst.js:57:5:57:10 | 12_300 |
| tst.js:58:1:58:12 | x = 12345_00 |
| tst.js:58:5:58:12 | 12345_00 |
| tst.js:59:1:59:12 | x = 123_4500 |
| tst.js:59:5:59:12 | 123_4500 |
| tst.js:60:1:60:13 | x = 1_234_500 |
| tst.js:60:5:60:13 | 1_234_500 |
| tst.js:61:1:61:10 | x = 1e1_00 |
| tst.js:61:5:61:10 | 1e1_00 |
| tst.js:62:1:62:14 | x = 0xaa_bb_cc |
| tst.js:62:5:62:14 | 0xaa_bb_cc |

View File

@@ -2,3 +2,9 @@ import javascript
from ConstantExpr c
select c
query int getIntValue(Expr e) { result = e.getIntValue() }
query float getFloatValue(NumberLiteral e) { result = e.getFloatValue() }
query string getLiteralValue(Literal lit) { result = lit.getValue() }

View File

@@ -48,3 +48,15 @@ x = 1;
x.p = 1;
x += 1;
x += x;
x = 1_000;
x = 1_000_123;
x = 1_000_000_000;
x = 101_475_938.38;
x = 123_00;
x = 12_300;
x = 12345_00;
x = 123_4500;
x = 1_234_500;
x = 1e1_00;
x = 0xaa_bb_cc;

View File

@@ -1 +1,2 @@
| tst.js:4:15:4:22 | "source" | tst.js:9:7:9:24 | readTaint(tainted) |
| tst.js:4:15:4:22 | "source" | tst.js:15:7:15:20 | tainted3.other |

View File

@@ -19,6 +19,18 @@ class Configuration extends TaintTracking::Configuration {
) and
prop = "bar"
}
// calling .copy("foo", "bar") actually moves a property from "foo" to "bar".
override predicate isAdditionalLoadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
exists(DataFlow::MethodCallNode call |
call.getMethodName() = "copy" and call = succ and pred = call.getReceiver()
|
call.getArgument(0).mayHaveStringValue(loadProp) and
call.getArgument(1).mayHaveStringValue(storeProp)
)
}
}
from DataFlow::Node pred, DataFlow::Node succ, Configuration cfg

View File

@@ -6,5 +6,15 @@
function readTaint(x) {
return x.foo;
}
sink(readTaint(tainted));
sink(readTaint(tainted)); // NOT OK
var tainted2 = {myProp: source};
var tainted3 = tainted2.copy("myProp", "other");
sink(tainted3.other); // NOT OK.
var tainted4 = tainted2.copy("other", "myProp"); // does nothing, there is no "other" on tainted2.
sink(tainted4.other); // OK.
})();

View File

@@ -35,6 +35,36 @@
| sources.js:11:14:11:16 | key | sources.js:11:14:11:16 | key |
| sources.js:11:23:11:27 | array | sources.js:11:23:11:27 | array |
| sources.js:11:32:11:34 | key | sources.js:11:32:11:34 | key |
| tst2.ts:1:18:1:18 | A | tst2.ts:1:18:1:18 | A |
| tst2.ts:2:14:2:14 | x | tst2.ts:2:14:2:14 | x |
| tst2.ts:2:14:2:19 | x = 42 | tst2.ts:2:14:2:19 | x = 42 |
| tst2.ts:2:18:2:19 | 42 | tst2.ts:2:18:2:19 | 42 |
| tst2.ts:3:3:3:6 | setX | tst2.ts:3:3:3:6 | setX |
| tst2.ts:3:3:3:8 | setX() | tst2.ts:3:3:3:8 | setX() |
| tst2.ts:4:3:4:3 | x | tst2.ts:4:3:4:3 | x |
| tst2.ts:7:10:7:13 | setX | tst2.ts:7:10:7:13 | setX |
| tst2.ts:8:3:8:3 | A | tst2.ts:8:3:8:3 | A |
| tst2.ts:8:3:8:5 | A.x | tst2.ts:8:3:8:5 | A.x |
| tst2.ts:8:3:8:10 | A.x = 23 | tst2.ts:8:3:8:10 | A.x = 23 |
| tst2.ts:8:5:8:5 | x | tst2.ts:8:5:8:5 | x |
| tst2.ts:8:9:8:10 | 23 | tst2.ts:8:9:8:10 | 23 |
| tst2.ts:11:5:11:7 | nd2 | tst2.ts:11:5:11:7 | nd2 |
| tst2.ts:11:5:11:23 | nd2 = A.x as number | tst2.ts:11:5:11:23 | nd2 = A.x as number |
| tst2.ts:11:11:11:11 | A | tst2.ts:11:11:11:11 | A |
| tst2.ts:11:11:11:13 | A.x | tst2.ts:11:11:11:13 | A.x |
| tst2.ts:11:11:11:23 | A.x as number | tst2.ts:11:11:11:23 | A.x as number |
| tst2.ts:11:13:11:13 | x | tst2.ts:11:13:11:13 | x |
| tst2.ts:13:7:13:16 | StringList | tst2.ts:13:7:13:16 | StringList |
| tst2.ts:13:26:13:29 | List | tst2.ts:13:26:13:29 | List |
| tst2.ts:13:26:13:37 | List<string> | tst2.ts:13:26:13:37 | List<string> |
| tst2.ts:13:39:13:38 | (...arg ... rgs); } | tst2.ts:13:39:13:38 | (...arg ... rgs); } |
| tst2.ts:13:39:13:38 | ...args | tst2.ts:13:39:13:38 | ...args |
| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args |
| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args |
| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args |
| tst2.ts:13:39:13:38 | constructor | tst2.ts:13:39:13:38 | constructor |
| tst2.ts:13:39:13:38 | super | tst2.ts:13:39:13:38 | super |
| tst2.ts:13:39:13:38 | super(...args) | tst2.ts:13:39:13:38 | super(...args) |
| tst.js:1:10:1:11 | fs | tst.js:1:10:1:11 | fs |
| tst.js:1:10:1:11 | fs | tst.js:1:10:1:11 | fs |
| tst.js:1:10:1:11 | fs | tst.js:1:10:1:11 | fs |
@@ -289,33 +319,3 @@
| tst.js:115:1:115:12 | Array.call() | tst.js:115:1:115:12 | Array.call() |
| tst.js:115:1:115:12 | reflective call | tst.js:115:1:115:12 | Array.call() |
| tst.js:115:7:115:10 | call | tst.js:115:7:115:10 | call |
| tst.ts:1:18:1:18 | A | tst.ts:1:18:1:18 | A |
| tst.ts:2:14:2:14 | x | tst.ts:2:14:2:14 | x |
| tst.ts:2:14:2:19 | x = 42 | tst.ts:2:14:2:19 | x = 42 |
| tst.ts:2:18:2:19 | 42 | tst.ts:2:18:2:19 | 42 |
| tst.ts:3:3:3:6 | setX | tst.ts:3:3:3:6 | setX |
| tst.ts:3:3:3:8 | setX() | tst.ts:3:3:3:8 | setX() |
| tst.ts:4:3:4:3 | x | tst.ts:4:3:4:3 | x |
| tst.ts:7:10:7:13 | setX | tst.ts:7:10:7:13 | setX |
| tst.ts:8:3:8:3 | A | tst.ts:8:3:8:3 | A |
| tst.ts:8:3:8:5 | A.x | tst.ts:8:3:8:5 | A.x |
| tst.ts:8:3:8:10 | A.x = 23 | tst.ts:8:3:8:10 | A.x = 23 |
| tst.ts:8:5:8:5 | x | tst.ts:8:5:8:5 | x |
| tst.ts:8:9:8:10 | 23 | tst.ts:8:9:8:10 | 23 |
| tst.ts:11:5:11:7 | nd2 | tst.ts:11:5:11:7 | nd2 |
| tst.ts:11:5:11:23 | nd2 = A.x as number | tst.ts:11:5:11:23 | nd2 = A.x as number |
| tst.ts:11:11:11:11 | A | tst.ts:11:11:11:11 | A |
| tst.ts:11:11:11:13 | A.x | tst.ts:11:11:11:13 | A.x |
| tst.ts:11:11:11:23 | A.x as number | tst.ts:11:11:11:23 | A.x as number |
| tst.ts:11:13:11:13 | x | tst.ts:11:13:11:13 | x |
| tst.ts:13:7:13:16 | StringList | tst.ts:13:7:13:16 | StringList |
| tst.ts:13:26:13:29 | List | tst.ts:13:26:13:29 | List |
| tst.ts:13:26:13:37 | List<string> | tst.ts:13:26:13:37 | List<string> |
| tst.ts:13:39:13:38 | (...arg ... rgs); } | tst.ts:13:39:13:38 | (...arg ... rgs); } |
| tst.ts:13:39:13:38 | ...args | tst.ts:13:39:13:38 | ...args |
| tst.ts:13:39:13:38 | args | tst.ts:13:39:13:38 | args |
| tst.ts:13:39:13:38 | args | tst.ts:13:39:13:38 | args |
| tst.ts:13:39:13:38 | args | tst.ts:13:39:13:38 | args |
| tst.ts:13:39:13:38 | constructor | tst.ts:13:39:13:38 | constructor |
| tst.ts:13:39:13:38 | super | tst.ts:13:39:13:38 | super |
| tst.ts:13:39:13:38 | super(...args) | tst.ts:13:39:13:38 | super(...args) |

View File

@@ -12,6 +12,20 @@
| sources.js:10:12:10:14 | key | sources.js:10:28:10:30 | key |
| sources.js:11:12:11:18 | key | sources.js:11:32:11:34 | key |
| sources.js:11:14:11:16 | key | sources.js:11:12:11:18 | key |
| tst2.ts:1:1:1:1 | A | tst2.ts:1:18:1:18 | A |
| tst2.ts:1:1:1:1 | A | tst2.ts:7:1:7:0 | A |
| tst2.ts:1:8:5:1 | A | tst2.ts:7:1:7:0 | A |
| tst2.ts:1:8:5:1 | A | tst2.ts:11:11:11:11 | A |
| tst2.ts:1:8:5:1 | namespa ... lysed\\n} | tst2.ts:1:8:5:1 | A |
| tst2.ts:2:14:2:19 | x | tst2.ts:4:3:4:3 | x |
| tst2.ts:2:18:2:19 | 42 | tst2.ts:2:14:2:19 | x |
| tst2.ts:7:1:7:0 | A | tst2.ts:8:3:8:3 | A |
| tst2.ts:7:1:9:1 | functio ... = 23;\\n} | tst2.ts:7:10:7:13 | setX |
| tst2.ts:7:10:7:13 | setX | tst2.ts:3:3:3:6 | setX |
| tst2.ts:8:9:8:10 | 23 | tst2.ts:8:3:8:10 | A.x = 23 |
| tst2.ts:11:11:11:13 | A.x | tst2.ts:11:11:11:23 | A.x as number |
| tst2.ts:13:26:13:29 | List | tst2.ts:13:26:13:37 | List<string> |
| tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args |
| tst.js:1:1:1:1 | x | tst.js:28:2:28:1 | x |
| tst.js:1:1:1:1 | x | tst.js:32:1:32:0 | x |
| tst.js:1:10:1:11 | fs | tst.js:1:10:1:11 | fs |
@@ -147,17 +161,3 @@
| tst.js:111:29:111:31 | o2c | tst.js:111:6:111:38 | v2c |
| tst.js:111:36:111:38 | o2d | tst.js:111:6:111:32 | [v2a, v ... = o2c] |
| tst.js:115:1:115:12 | reflective call | tst.js:115:1:115:12 | Array.call() |
| tst.ts:1:1:1:1 | A | tst.ts:1:18:1:18 | A |
| tst.ts:1:1:1:1 | A | tst.ts:7:1:7:0 | A |
| tst.ts:1:8:5:1 | A | tst.ts:7:1:7:0 | A |
| tst.ts:1:8:5:1 | A | tst.ts:11:11:11:11 | A |
| tst.ts:1:8:5:1 | namespa ... lysed\\n} | tst.ts:1:8:5:1 | A |
| tst.ts:2:14:2:19 | x | tst.ts:4:3:4:3 | x |
| tst.ts:2:18:2:19 | 42 | tst.ts:2:14:2:19 | x |
| tst.ts:7:1:7:0 | A | tst.ts:8:3:8:3 | A |
| tst.ts:7:1:9:1 | functio ... = 23;\\n} | tst.ts:7:10:7:13 | setX |
| tst.ts:7:10:7:13 | setX | tst.ts:3:3:3:6 | setX |
| tst.ts:8:9:8:10 | 23 | tst.ts:8:3:8:10 | A.x = 23 |
| tst.ts:11:11:11:13 | A.x | tst.ts:11:11:11:23 | A.x as number |
| tst.ts:13:26:13:29 | List | tst.ts:13:26:13:37 | List<string> |
| tst.ts:13:39:13:38 | args | tst.ts:13:39:13:38 | args |

View File

@@ -1,6 +1,8 @@
| eval.js:2:11:2:12 | 42 | 42 |
| sources.js:4:12:4:13 | 19 | 19 |
| sources.js:5:4:5:5 | 23 | 23 |
| tst2.ts:2:18:2:19 | 42 | 42 |
| tst2.ts:8:9:8:10 | 23 | 23 |
| tst.js:3:9:3:10 | 42 | 42 |
| tst.js:51:11:51:12 | 42 | 42 |
| tst.js:65:9:65:10 | 42 | 42 |
@@ -11,5 +13,3 @@
| tst.js:103:6:103:7 | 19 | 19 |
| tst.js:103:10:103:11 | 23 | 23 |
| tst.js:103:14:103:14 | 0 | 0 |
| tst.ts:2:18:2:19 | 42 | 42 |
| tst.ts:8:9:8:10 | 23 | 23 |

View File

@@ -13,6 +13,18 @@
| sources.js:10:12:10:14 | key | heap |
| sources.js:11:12:11:18 | key | heap |
| sources.js:11:14:11:16 | key | heap |
| tst2.ts:2:14:2:19 | x | namespace |
| tst2.ts:3:3:3:8 | exceptional return of setX() | call |
| tst2.ts:3:3:3:8 | setX() | call |
| tst2.ts:7:1:9:1 | exceptional return of function setX | call |
| tst2.ts:8:3:8:5 | A.x | heap |
| tst2.ts:11:11:11:13 | A.x | heap |
| tst2.ts:13:26:13:29 | List | global |
| tst2.ts:13:39:13:38 | args | call |
| tst2.ts:13:39:13:38 | exceptional return of default constructor of class StringList | call |
| tst2.ts:13:39:13:38 | exceptional return of super(...args) | call |
| tst2.ts:13:39:13:38 | super | call |
| tst2.ts:13:39:13:38 | super(...args) | call |
| tst.js:1:10:1:11 | fs | import |
| tst.js:16:1:20:9 | exceptional return of (functi ... ("arg") | call |
| tst.js:16:2:20:1 | exceptional return of function f | call |
@@ -92,15 +104,3 @@
| tst.js:115:1:115:10 | Array.call | heap |
| tst.js:115:1:115:12 | Array.call() | call |
| tst.js:115:1:115:12 | exceptional return of Array.call() | call |
| tst.ts:2:14:2:19 | x | namespace |
| tst.ts:3:3:3:8 | exceptional return of setX() | call |
| tst.ts:3:3:3:8 | setX() | call |
| tst.ts:7:1:9:1 | exceptional return of function setX | call |
| tst.ts:8:3:8:5 | A.x | heap |
| tst.ts:11:11:11:13 | A.x | heap |
| tst.ts:13:26:13:29 | List | global |
| tst.ts:13:39:13:38 | args | call |
| tst.ts:13:39:13:38 | exceptional return of default constructor of class StringList | call |
| tst.ts:13:39:13:38 | exceptional return of super(...args) | call |
| tst.ts:13:39:13:38 | super | call |
| tst.ts:13:39:13:38 | super(...args) | call |

View File

@@ -1,8 +1,8 @@
| sources.js:1:6:1:6 | x |
| sources.js:3:11:3:11 | x |
| sources.js:9:14:9:18 | array |
| tst2.ts:13:39:13:38 | args |
| tst.js:16:13:16:13 | a |
| tst.js:32:12:32:12 | b |
| tst.js:87:11:87:24 | { p: x, ...o } |
| tst.js:98:11:98:24 | [ x, ...rest ] |
| tst.ts:13:39:13:38 | args |

View File

@@ -19,6 +19,18 @@
| sources.js:10:12:10:14 | key |
| sources.js:11:12:11:18 | { key } |
| sources.js:11:14:11:16 | key |
| tst2.ts:1:1:1:0 | this |
| tst2.ts:3:3:3:8 | setX() |
| tst2.ts:7:1:7:0 | this |
| tst2.ts:7:1:9:1 | functio ... = 23;\\n} |
| tst2.ts:8:3:8:5 | A.x |
| tst2.ts:11:11:11:13 | A.x |
| tst2.ts:13:1:13:40 | class S ... ing> {} |
| tst2.ts:13:26:13:29 | List |
| tst2.ts:13:39:13:38 | (...arg ... rgs); } |
| tst2.ts:13:39:13:38 | args |
| tst2.ts:13:39:13:38 | super(...args) |
| tst2.ts:13:39:13:38 | this |
| tst.js:1:1:1:0 | this |
| tst.js:1:1:1:24 | import ... m 'fs'; |
| tst.js:1:10:1:11 | fs |
@@ -105,15 +117,3 @@
| tst.js:115:1:115:10 | Array.call |
| tst.js:115:1:115:12 | Array.call() |
| tst.js:115:1:115:12 | reflective call |
| tst.ts:1:1:1:0 | this |
| tst.ts:3:3:3:8 | setX() |
| tst.ts:7:1:7:0 | this |
| tst.ts:7:1:9:1 | functio ... = 23;\\n} |
| tst.ts:8:3:8:5 | A.x |
| tst.ts:11:11:11:13 | A.x |
| tst.ts:13:1:13:40 | class S ... ing> {} |
| tst.ts:13:26:13:29 | List |
| tst.ts:13:39:13:38 | (...arg ... rgs); } |
| tst.ts:13:39:13:38 | args |
| tst.ts:13:39:13:38 | super(...args) |
| tst.ts:13:39:13:38 | this |

View File

@@ -1,15 +1,15 @@
| tst2.ts:7:9:7:9 | a | 0 | tst2.ts:7:6:7:7 | @A |
| tst2.ts:8:12:8:13 | ab | 0 | tst2.ts:8:6:8:7 | @A |
| tst2.ts:8:12:8:13 | ab | 1 | tst2.ts:8:9:8:10 | @B |
| tst2.ts:10:15:10:15 | a | 0 | tst2.ts:10:12:10:13 | @A |
| tst2.ts:11:18:11:19 | ab | 0 | tst2.ts:11:12:11:13 | @A |
| tst2.ts:11:18:11:19 | ab | 1 | tst2.ts:11:15:11:16 | @B |
| tst2.ts:13:9:13:9 | a | 0 | tst2.ts:13:6:13:7 | @A |
| tst2.ts:13:15:13:15 | b | 0 | tst2.ts:13:12:13:13 | @B |
| tst2.ts:14:9:14:9 | a | 0 | tst2.ts:14:6:14:7 | @A |
| tst2.ts:14:18:14:19 | bc | 0 | tst2.ts:14:12:14:13 | @B |
| tst2.ts:14:18:14:19 | bc | 1 | tst2.ts:14:15:14:16 | @C |
| tst.js:1:7:3:1 | class C ... ) { }\\n} | 0 | tst.js:1:1:1:2 | @A |
| tst.js:1:7:3:1 | class C ... ) { }\\n} | 1 | tst.js:1:4:1:5 | @B |
| tst.js:2:19:2:25 | m() { } | 0 | tst.js:2:3:2:17 | @testable(true) |
| tst.js:7:3:7:25 | get bar ... rn 42 } | 0 | tst.js:6:3:6:6 | @Foo |
| tst.ts:7:9:7:9 | a | 0 | tst.ts:7:6:7:7 | @A |
| tst.ts:8:12:8:13 | ab | 0 | tst.ts:8:6:8:7 | @A |
| tst.ts:8:12:8:13 | ab | 1 | tst.ts:8:9:8:10 | @B |
| tst.ts:10:15:10:15 | a | 0 | tst.ts:10:12:10:13 | @A |
| tst.ts:11:18:11:19 | ab | 0 | tst.ts:11:12:11:13 | @A |
| tst.ts:11:18:11:19 | ab | 1 | tst.ts:11:15:11:16 | @B |
| tst.ts:13:9:13:9 | a | 0 | tst.ts:13:6:13:7 | @A |
| tst.ts:13:15:13:15 | b | 0 | tst.ts:13:12:13:13 | @B |
| tst.ts:14:9:14:9 | a | 0 | tst.ts:14:6:14:7 | @A |
| tst.ts:14:18:14:19 | bc | 0 | tst.ts:14:12:14:13 | @B |
| tst.ts:14:18:14:19 | bc | 1 | tst.ts:14:15:14:16 | @C |

View File

@@ -1,15 +1,15 @@
| tst2.ts:7:6:7:7 | @A | tst2.ts:7:7:7:7 | A | tst2.ts:7:9:7:9 | a |
| tst2.ts:8:6:8:7 | @A | tst2.ts:8:7:8:7 | A | tst2.ts:8:12:8:13 | ab |
| tst2.ts:8:9:8:10 | @B | tst2.ts:8:10:8:10 | B | tst2.ts:8:12:8:13 | ab |
| tst2.ts:10:12:10:13 | @A | tst2.ts:10:13:10:13 | A | tst2.ts:10:15:10:15 | a |
| tst2.ts:11:12:11:13 | @A | tst2.ts:11:13:11:13 | A | tst2.ts:11:18:11:19 | ab |
| tst2.ts:11:15:11:16 | @B | tst2.ts:11:16:11:16 | B | tst2.ts:11:18:11:19 | ab |
| tst2.ts:13:6:13:7 | @A | tst2.ts:13:7:13:7 | A | tst2.ts:13:9:13:9 | a |
| tst2.ts:13:12:13:13 | @B | tst2.ts:13:13:13:13 | B | tst2.ts:13:15:13:15 | b |
| tst2.ts:14:6:14:7 | @A | tst2.ts:14:7:14:7 | A | tst2.ts:14:9:14:9 | a |
| tst2.ts:14:12:14:13 | @B | tst2.ts:14:13:14:13 | B | tst2.ts:14:18:14:19 | bc |
| tst2.ts:14:15:14:16 | @C | tst2.ts:14:16:14:16 | C | tst2.ts:14:18:14:19 | bc |
| tst.js:1:1:1:2 | @A | tst.js:1:2:1:2 | A | tst.js:1:7:3:1 | class C ... ) { }\\n} |
| tst.js:1:4:1:5 | @B | tst.js:1:5:1:5 | B | tst.js:1:7:3:1 | class C ... ) { }\\n} |
| tst.js:2:3:2:17 | @testable(true) | tst.js:2:4:2:17 | testable(true) | tst.js:2:19:2:25 | m() { } |
| tst.js:6:3:6:6 | @Foo | tst.js:6:4:6:6 | Foo | tst.js:7:3:7:25 | get bar ... rn 42 } |
| tst.ts:7:6:7:7 | @A | tst.ts:7:7:7:7 | A | tst.ts:7:9:7:9 | a |
| tst.ts:8:6:8:7 | @A | tst.ts:8:7:8:7 | A | tst.ts:8:12:8:13 | ab |
| tst.ts:8:9:8:10 | @B | tst.ts:8:10:8:10 | B | tst.ts:8:12:8:13 | ab |
| tst.ts:10:12:10:13 | @A | tst.ts:10:13:10:13 | A | tst.ts:10:15:10:15 | a |
| tst.ts:11:12:11:13 | @A | tst.ts:11:13:11:13 | A | tst.ts:11:18:11:19 | ab |
| tst.ts:11:15:11:16 | @B | tst.ts:11:16:11:16 | B | tst.ts:11:18:11:19 | ab |
| tst.ts:13:6:13:7 | @A | tst.ts:13:7:13:7 | A | tst.ts:13:9:13:9 | a |
| tst.ts:13:12:13:13 | @B | tst.ts:13:13:13:13 | B | tst.ts:13:15:13:15 | b |
| tst.ts:14:6:14:7 | @A | tst.ts:14:7:14:7 | A | tst.ts:14:9:14:9 | a |
| tst.ts:14:12:14:13 | @B | tst.ts:14:13:14:13 | B | tst.ts:14:18:14:19 | bc |
| tst.ts:14:15:14:16 | @C | tst.ts:14:16:14:16 | C | tst.ts:14:18:14:19 | bc |

View File

@@ -1,14 +1,14 @@
| jsTest.js:0:0:0:0 | jsTest.js | javascript |
| jsxTest.jsx:0:0:0:0 | jsxTest.jsx | javascript |
| mjsTest.mjs:0:0:0:0 | mjsTest.mjs | javascript |
| test.htm:0:0:0:0 | test.htm | html |
| test.html:0:0:0:0 | test.html | html |
| test.js:0:0:0:0 | test.js | javascript |
| test.json:0:0:0:0 | test.json | json |
| test.jsx:0:0:0:0 | test.jsx | javascript |
| test.mjs:0:0:0:0 | test.mjs | javascript |
| test.raml:0:0:0:0 | test.raml | yaml |
| test.ts:0:0:0:0 | test.ts | typescript |
| test.tsx:0:0:0:0 | test.tsx | typescript |
| test.vue:0:0:0:0 | test.vue | html |
| test.xhtm:0:0:0:0 | test.xhtm | html |
| test.xhtml:0:0:0:0 | test.xhtml | html |
| test.yaml:0:0:0:0 | test.yaml | yaml |
| test.yml:0:0:0:0 | test.yml | yaml |
| tsTest.ts:0:0:0:0 | tsTest.ts | typescript |
| tsxTest.tsx:0:0:0:0 | tsxTest.tsx | typescript |

View File

@@ -272,6 +272,10 @@
| ts.ts:1:16:1:25 | class A |
| ts.ts:1:16:1:25 | instance of class A |
| ts.ts:1:24:1:23 | default constructor of class A |
| tsTest.ts:1:1:13:0 | exports object of module tsTest |
| tsTest.ts:1:1:13:0 | module object of module tsTest |
| tsTest.ts:8:1:10:1 | function setX |
| tsTest.ts:8:1:10:1 | instance of function setX |
| tst2.js:3:2:5:1 | anonymous function |
| tst2.js:3:2:5:1 | instance of anonymous function |
| tst.js:1:1:39:1 | arguments object of function tst |
@@ -341,9 +345,5 @@
| tst.js:174:1:183:1 | function awaitFlow |
| tst.mjs:1:1:4:0 | exports object of module tst |
| tst.mjs:1:1:4:0 | module object of module tst |
| tst.ts:1:1:13:0 | exports object of module tst |
| tst.ts:1:1:13:0 | module object of module tst |
| tst.ts:8:1:10:1 | function setX |
| tst.ts:8:1:10:1 | instance of function setX |
| with.js:1:1:17:1 | function f |
| with.js:1:1:17:1 | instance of function f |

View File

@@ -281,6 +281,10 @@
| refinements.js:61:7:61:8 | x2 | refinements.js:61:12:61:12 | x | file://:0:0:0:0 | "" |
| refinements.js:61:7:61:8 | x2 | refinements.js:61:12:61:12 | x | file://:0:0:0:0 | non-empty, non-numeric string |
| refinements.js:61:7:61:8 | x2 | refinements.js:61:12:61:12 | x | file://:0:0:0:0 | numeric string |
| tsTest.ts:2:14:2:14 | x | tsTest.ts:2:18:2:19 | 42 | file://:0:0:0:0 | non-zero value |
| tsTest.ts:4:7:4:8 | x2 | tsTest.ts:4:12:4:12 | x | file://:0:0:0:0 | indefinite value (namespace) |
| tsTest.ts:4:7:4:8 | x2 | tsTest.ts:4:12:4:12 | x | file://:0:0:0:0 | non-zero value |
| tsTest.ts:12:5:12:5 | a | tsTest.ts:12:9:12:9 | A | file://:0:0:0:0 | object |
| tst2.js:4:7:4:7 | x | tst2.js:4:11:4:20 | someGlobal | file://:0:0:0:0 | "" |
| tst2.js:4:7:4:7 | x | tst2.js:4:11:4:20 | someGlobal | file://:0:0:0:0 | indefinite value (global) |
| tst.js:3:7:3:8 | x1 | tst.js:3:12:3:15 | true | file://:0:0:0:0 | true |
@@ -462,10 +466,6 @@
| tst.js:186:5:186:6 | x1 | tst.js:186:10:186:24 | someOtherGlobal | file://:0:0:0:0 | indefinite value (global) |
| tst.js:186:5:186:6 | x1 | tst.js:186:10:186:24 | someOtherGlobal | file://:0:0:0:0 | indefinite value (heap) |
| tst.mjs:3:5:3:7 | req | tst.mjs:3:11:3:17 | require | file://:0:0:0:0 | indefinite value (global) |
| tst.ts:2:14:2:14 | x | tst.ts:2:18:2:19 | 42 | file://:0:0:0:0 | non-zero value |
| tst.ts:4:7:4:8 | x2 | tst.ts:4:12:4:12 | x | file://:0:0:0:0 | indefinite value (namespace) |
| tst.ts:4:7:4:8 | x2 | tst.ts:4:12:4:12 | x | file://:0:0:0:0 | non-zero value |
| tst.ts:12:5:12:5 | a | tst.ts:12:9:12:9 | A | file://:0:0:0:0 | object |
| with.js:2:7:2:7 | x | with.js:2:11:2:12 | 42 | file://:0:0:0:0 | non-zero value |
| with.js:2:15:2:15 | y | with.js:2:19:2:22 | null | file://:0:0:0:0 | null |
| with.js:4:9:4:10 | r1 | with.js:4:14:4:14 | x | file://:0:0:0:0 | indefinite value (eval) |

View File

@@ -58,6 +58,7 @@
| refinements.js:44:3:48:3 | instance of function inner | refinements.js:44:3:48:3 | instance of function inner |
| refinements.js:58:1:62:1 | instance of function f6 | refinements.js:58:1:62:1 | instance of function f6 |
| ts2.ts:1:10:1:22 | instance of anonymous function | ts2.ts:1:10:1:22 | instance of anonymous function |
| tsTest.ts:8:1:10:1 | instance of function setX | tsTest.ts:8:1:10:1 | instance of function setX |
| tst2.js:3:2:5:1 | instance of anonymous function | tst2.js:3:2:5:1 | instance of anonymous function |
| tst.js:1:1:39:1 | instance of function tst | tst.js:1:1:39:1 | instance of function tst |
| tst.js:15:12:15:23 | instance of function xd | tst.js:15:12:15:23 | instance of function xd |
@@ -88,5 +89,4 @@
| tst.js:144:1:149:1 | instance of function tst3 | tst.js:144:1:149:1 | instance of function tst3 |
| tst.js:151:1:162:1 | instance of function tst4 | tst.js:151:1:162:1 | instance of function tst4 |
| tst.js:164:1:172:1 | instance of function tst5 | tst.js:164:1:172:1 | instance of function tst5 |
| tst.ts:8:1:10:1 | instance of function setX | tst.ts:8:1:10:1 | instance of function setX |
| with.js:1:1:17:1 | instance of function f | with.js:1:1:17:1 | instance of function f |

View File

@@ -156,6 +156,9 @@
| refinements.js:53:7:53:8 | x5 | refinements.js:53:12:53:12 | f | undefined |
| refinements.js:55:7:55:8 | x6 | refinements.js:55:12:55:12 | f | null or undefined |
| refinements.js:61:7:61:8 | x2 | refinements.js:61:12:61:12 | x | string |
| tsTest.ts:2:14:2:14 | x | tsTest.ts:2:18:2:19 | 42 | number |
| tsTest.ts:4:7:4:8 | x2 | tsTest.ts:4:12:4:12 | x | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| tsTest.ts:12:5:12:5 | a | tsTest.ts:12:9:12:9 | A | object |
| tst2.js:4:7:4:7 | x | tst2.js:4:11:4:20 | someGlobal | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| tst.js:3:7:3:8 | x1 | tst.js:3:12:3:15 | true | boolean |
| tst.js:4:7:4:8 | x2 | tst.js:4:12:4:16 | false | boolean |
@@ -239,9 +242,6 @@
| tst.js:185:5:185:21 | [someOtherGlobal] | tst.js:185:25:185:26 | [] | object |
| tst.js:186:5:186:6 | x1 | tst.js:186:10:186:24 | someOtherGlobal | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| tst.mjs:3:5:3:7 | req | tst.mjs:3:11:3:17 | require | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| tst.ts:2:14:2:14 | x | tst.ts:2:18:2:19 | 42 | number |
| tst.ts:4:7:4:8 | x2 | tst.ts:4:12:4:12 | x | boolean, class, date, function, null, number, object, regular expression,string or undefined |
| tst.ts:12:5:12:5 | a | tst.ts:12:9:12:9 | A | object |
| with.js:2:7:2:7 | x | with.js:2:11:2:12 | 42 | number |
| with.js:2:15:2:15 | y | with.js:2:19:2:22 | null | null |
| with.js:4:9:4:10 | r1 | with.js:4:14:4:14 | x | boolean, class, date, function, null, number, object, regular expression,string or undefined |

View File

@@ -1,5 +1,5 @@
| b | src/node_modules/b/lib/index.js:1:1:2:0 | <toplevel> |
| b | src/node_modules/b/lib/index.ts:1:1:2:0 | <toplevel> |
| b | src/node_modules/b/lib/util.ts:1:1:2:0 | <toplevel> |
| c | src/node_modules/c/src/index.js:1:1:2:0 | <toplevel> |
| d | src/node_modules/d/main.js:1:1:2:0 | <toplevel> |
| test-package | src/index.js:1:1:4:0 | <toplevel> |

View File

@@ -1,4 +1,4 @@
| b | src/node_modules/b/lib/index.ts:1:1:2:0 | <toplevel> |
| b | src/node_modules/b/lib/index.js:1:1:2:0 | <toplevel> |
| c | src/node_modules/c/src/index.js:1:1:2:0 | <toplevel> |
| d | src/node_modules/d/main.js:1:1:2:0 | <toplevel> |
| test-package | src/index.js:1:1:4:0 | <toplevel> |

View File

@@ -6,3 +6,8 @@ function f() {
if (two > one) {} // NOT OK - always true
if (two <= one) {} // NOT OK - always false
}
function underscores(x) {
if (x >= 1_000_000) return; // OK
if (x >= 1_000) return; // OK
}

View File

@@ -0,0 +1 @@
export default 45;

View File

@@ -0,0 +1 @@
export default 45 as number;

View File

@@ -0,0 +1 @@
| index.ts:0:0:0:0 | index.ts |

View File

@@ -0,0 +1,3 @@
import javascript
query File files() { any() }

View File

@@ -1,5 +1,3 @@
| electron.d.ts:2:16:2:28 | BrowserWindow |
| electron.d.ts:3:16:3:26 | BrowserView |
| electron.js:3:5:3:48 | bw |
| electron.js:3:10:3:48 | new Bro ... s: {}}) |
| electron.js:4:5:4:46 | bv |
@@ -14,7 +12,9 @@
| electron.js:62:13:62:59 | new Bro ... 1500 }) |
| electron.js:63:3:63:5 | win |
| electron.js:65:18:65:20 | win |
| electron.ts:3:12:3:13 | bw |
| electron.ts:3:40:3:41 | bv |
| electron.ts:4:3:4:4 | bw |
| electron.ts:5:3:5:4 | bv |
| electronTs.d.ts:2:16:2:28 | BrowserWindow |
| electronTs.d.ts:3:16:3:26 | BrowserView |
| electronTs.ts:3:12:3:13 | bw |
| electronTs.ts:3:40:3:41 | bv |
| electronTs.ts:4:3:4:4 | bw |
| electronTs.ts:5:3:5:4 | bv |

View File

@@ -1,5 +1,5 @@
| electron.js:39:1:39:19 | foo(bw).webContents |
| electron.js:40:1:40:19 | foo(bv).webContents |
| electron.js:65:18:65:32 | win.webContents |
| electron.ts:4:3:4:16 | bw.webContents |
| electron.ts:5:3:5:16 | bv.webContents |
| electronTs.ts:4:3:4:16 | bw.webContents |
| electronTs.ts:5:3:5:16 | bv.webContents |

View File

@@ -1,4 +1,4 @@
///<reference path="./electron.d.ts"/>
///<reference path="./electronTs.d.ts"/>
function f(bw: Electron.BrowserWindow, bv: Electron.BrowserView) {
bw.webContents;

View File

@@ -1,4 +1,4 @@
| tst2.js:0:0:0:0 | tst2.js |
| tst3.ts:0:0:0:0 | tst3.ts |
| tst.html:0:0:0:0 | tst.html |
| tst.js:0:0:0:0 | tst.js |
| tst.ts:0:0:0:0 | tst.ts |

View File

@@ -1,4 +1,4 @@
| tst2.js:0:0:0:0 | tst2.js | 26 |
| tst3.ts:0:0:0:0 | tst3.ts | 31 |
| tst.html:0:0:0:0 | tst.html | 127 |
| tst.js:0:0:0:0 | tst.js | 26 |
| tst.ts:0:0:0:0 | tst.ts | 31 |

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