mirror of
https://github.com/github/codeql.git
synced 2026-07-20 18:58:36 +02:00
Merge branch 'master' into init-dynamic-alloc-newexpr
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
140
config/sync-files.py
Normal 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()
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()) }
|
||||
|
||||
@@ -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`.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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), ", ")
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
@@ -1,4 +0,0 @@
|
||||
import cpp
|
||||
|
||||
from NewOrNewArrayExpr new
|
||||
select new, new.getPlacementPointer() as placement
|
||||
@@ -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 | |
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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>`__ | |
|
||||
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
@@ -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
|
||||
========================
|
||||
|
||||
@@ -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)*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
55
java/ql/src/experimental/Security/CWE/CWE-036/OpenStream.ql
Normal file
55
java/ql/src/experimental/Security/CWE/CWE-036/OpenStream.ql
Normal 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"
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -482,6 +482,7 @@ public class ESNextParser extends JSXParser {
|
||||
|
||||
if (code == '_') {
|
||||
if (underscoreAllowed) {
|
||||
seenUnderscoreNumericSeparator = true;
|
||||
// no adjacent underscores
|
||||
underscoreAllowed = false;
|
||||
++this.pos;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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%")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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$" }
|
||||
}
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
})();
|
||||
@@ -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) |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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) |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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> |
|
||||
|
||||
@@ -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> |
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export default 45;
|
||||
@@ -0,0 +1 @@
|
||||
export default 45 as number;
|
||||
@@ -0,0 +1 @@
|
||||
| index.ts:0:0:0:0 | index.ts |
|
||||
@@ -0,0 +1,3 @@
|
||||
import javascript
|
||||
|
||||
query File files() { any() }
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
///<reference path="./electron.d.ts"/>
|
||||
///<reference path="./electronTs.d.ts"/>
|
||||
|
||||
function f(bw: Electron.BrowserWindow, bv: Electron.BrowserView) {
|
||||
bw.webContents;
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user