Merge branch 'main' into py/CsvInjection

This commit is contained in:
yoff
2022-05-30 13:41:31 +02:00
committed by GitHub
351 changed files with 10835 additions and 5351 deletions

View File

@@ -12,6 +12,7 @@ on:
paths: paths:
- "javascript/ql/experimental/adaptivethreatmodeling/**" - "javascript/ql/experimental/adaptivethreatmodeling/**"
- .github/workflows/js-ml-tests.yml - .github/workflows/js-ml-tests.yml
workflow_dispatch:
defaults: defaults:
run: run:

View File

@@ -525,7 +525,8 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll", "csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll", "java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll", "javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll" "ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll",
"python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll"
], ],
"IncompleteUrlSubstringSanitization": [ "IncompleteUrlSubstringSanitization": [
"javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll", "javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll",
@@ -543,7 +544,8 @@
], ],
"ApiGraphModels": [ "ApiGraphModels": [
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll", "javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll",
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll" "ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll",
"python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll"
], ],
"TaintedFormatStringQuery Ruby/JS": [ "TaintedFormatStringQuery Ruby/JS": [
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringQuery.qll", "javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringQuery.qll",

View File

@@ -17,6 +17,36 @@
import cpp import cpp
import semmle.code.cpp.dataflow.DataFlow import semmle.code.cpp.dataflow.DataFlow
/**
* A Linux system call.
*/
class SystemCallFunction extends Function {
SystemCallFunction() {
exists(MacroInvocation m |
m.getMacro().getName().matches("SYSCALL\\_DEFINE%") and
this = m.getEnclosingFunction()
)
}
}
/**
* A value that comes from a Linux system call (sources).
*/
class SystemCallSource extends DataFlow::Node {
SystemCallSource() {
exists(FunctionCall fc |
fc.getTarget() instanceof SystemCallFunction and
(
this.asDefiningArgument() = fc.getAnArgument().getAChild*() or
this.asExpr() = fc
)
)
}
}
/**
* Macros used to check the value (barriers).
*/
class WriteAccessCheckMacro extends Macro { class WriteAccessCheckMacro extends Macro {
VariableAccess va; VariableAccess va;
@@ -28,6 +58,9 @@ class WriteAccessCheckMacro extends Macro {
VariableAccess getArgument() { result = va } VariableAccess getArgument() { result = va }
} }
/**
* The `unsafe_put_user` macro and its uses (sinks).
*/
class UnSafePutUserMacro extends Macro { class UnSafePutUserMacro extends Macro {
PointerDereferenceExpr writeUserPtr; PointerDereferenceExpr writeUserPtr;
@@ -42,15 +75,13 @@ class UnSafePutUserMacro extends Macro {
} }
} }
class ExploitableUserModePtrParam extends Parameter { class ExploitableUserModePtrParam extends SystemCallSource {
ExploitableUserModePtrParam() { ExploitableUserModePtrParam() {
not exists(WriteAccessCheckMacro writeAccessCheck |
DataFlow::localFlow(DataFlow::parameterNode(this),
DataFlow::exprNode(writeAccessCheck.getArgument()))
) and
exists(UnSafePutUserMacro unsafePutUser | exists(UnSafePutUserMacro unsafePutUser |
DataFlow::localFlow(DataFlow::parameterNode(this), DataFlow::localFlow(this, DataFlow::exprNode(unsafePutUser.getUserModePtr()))
DataFlow::exprNode(unsafePutUser.getUserModePtr())) ) and
not exists(WriteAccessCheckMacro writeAccessCheck |
DataFlow::localFlow(this, DataFlow::exprNode(writeAccessCheck.getArgument()))
) )
} }
} }

View File

@@ -1 +1,3 @@
| test.cpp:14:16:14:16 | p | unsafe_put_user write user-mode pointer $@ without check. | test.cpp:14:16:14:16 | p | p | | test.cpp:20:21:20:22 | ref arg & ... | unsafe_put_user write user-mode pointer $@ without check. | test.cpp:20:21:20:22 | ref arg & ... | ref arg & ... |
| test.cpp:41:21:41:22 | ref arg & ... | unsafe_put_user write user-mode pointer $@ without check. | test.cpp:41:21:41:22 | ref arg & ... | ref arg & ... |
| test.cpp:69:21:69:27 | ref arg & ... | unsafe_put_user write user-mode pointer $@ without check. | test.cpp:69:21:69:27 | ref arg & ... | ref arg & ... |

View File

@@ -1,7 +1,11 @@
typedef unsigned long size_t; typedef unsigned long size_t;
void SYSC_SOMESYSTEMCALL(void *param); #define SYSCALL_DEFINE(name, ...) \
void do_sys_##name(); \
void sys_##name(...) { do_sys_##name(); } \
void do_sys_##name()
SYSCALL_DEFINE(somesystemcall, void *param) {};
bool user_access_begin_impl(const void *where, size_t sz); bool user_access_begin_impl(const void *where, size_t sz);
void user_access_end_impl(); void user_access_end_impl();
@@ -13,14 +17,14 @@ void unsafe_put_user_impl(int what, const void *where, size_t sz);
void test1(int p) void test1(int p)
{ {
SYSC_SOMESYSTEMCALL(&p); sys_somesystemcall(&p);
unsafe_put_user(123, &p); // BAD unsafe_put_user(123, &p); // BAD
} }
void test2(int p) void test2(int p)
{ {
SYSC_SOMESYSTEMCALL(&p); sys_somesystemcall(&p);
if (user_access_begin(&p, sizeof(p))) if (user_access_begin(&p, sizeof(p)))
{ {
@@ -34,16 +38,16 @@ void test3()
{ {
int v; int v;
SYSC_SOMESYSTEMCALL(&v); sys_somesystemcall(&v);
unsafe_put_user(123, &v); // BAD [NOT DETECTED] unsafe_put_user(123, &v); // BAD
} }
void test4() void test4()
{ {
int v; int v;
SYSC_SOMESYSTEMCALL(&v); sys_somesystemcall(&v);
if (user_access_begin(&v, sizeof(v))) if (user_access_begin(&v, sizeof(v)))
{ {
@@ -62,16 +66,16 @@ void test5()
{ {
data myData; data myData;
SYSC_SOMESYSTEMCALL(&myData); sys_somesystemcall(&myData);
unsafe_put_user(123, &(myData.x)); // BAD [NOT DETECTED] unsafe_put_user(123, &(myData.x)); // BAD
} }
void test6() void test6()
{ {
data myData; data myData;
SYSC_SOMESYSTEMCALL(&myData); sys_somesystemcall(&myData);
if (user_access_begin(&myData, sizeof(myData))) if (user_access_begin(&myData, sizeof(myData)))
{ {

View File

@@ -1,10 +1,27 @@
package,sink,source,summary,sink:code,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value package,sink,source,summary,sink:code,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value
Dapper,55,,,,,,55,,,, Dapper,55,,,,,,55,,,,
JsonToItemsTaskFactory,,,7,,,,,,,7,
Microsoft.ApplicationBlocks.Data,28,,,,,,28,,,, Microsoft.ApplicationBlocks.Data,28,,,,,,28,,,,
Microsoft.CSharp,,,24,,,,,,,24,
Microsoft.EntityFrameworkCore,6,,,,,,6,,,, Microsoft.EntityFrameworkCore,6,,,,,,6,,,,
Microsoft.Extensions.Primitives,,,54,,,,,,,54, Microsoft.Extensions.Caching.Distributed,,,15,,,,,,,15,
Microsoft.VisualBasic,,,4,,,,,,,,4 Microsoft.Extensions.Caching.Memory,,,46,,,,,,,45,1
Microsoft.Extensions.Configuration,,,83,,,,,,,80,3
Microsoft.Extensions.DependencyInjection,,,62,,,,,,,62,
Microsoft.Extensions.DependencyModel,,,12,,,,,,,12,
Microsoft.Extensions.FileProviders,,,15,,,,,,,15,
Microsoft.Extensions.FileSystemGlobbing,,,15,,,,,,,13,2
Microsoft.Extensions.Hosting,,,17,,,,,,,16,1
Microsoft.Extensions.Http,,,10,,,,,,,10,
Microsoft.Extensions.Logging,,,37,,,,,,,37,
Microsoft.Extensions.Options,,,8,,,,,,,8,
Microsoft.Extensions.Primitives,,,63,,,,,,,63,
Microsoft.Interop,,,27,,,,,,,27,
Microsoft.NET.Build.Tasks,,,1,,,,,,,1,
Microsoft.NETCore.Platforms.BuildTasks,,,4,,,,,,,4,
Microsoft.VisualBasic,,,9,,,,,,,5,4
Microsoft.Win32,,,8,,,,,,,8,
MySql.Data.MySqlClient,48,,,,,,48,,,, MySql.Data.MySqlClient,48,,,,,,48,,,,
Newtonsoft.Json,,,91,,,,,,,73,18 Newtonsoft.Json,,,91,,,,,,,73,18
ServiceStack,194,,7,27,,75,92,,,7, ServiceStack,194,,7,27,,75,92,,,7,
System,28,3,2336,,4,,23,1,3,611,1725 System,28,3,12038,,4,,23,1,3,10096,1942
1 package sink source summary sink:code sink:html sink:remote sink:sql sink:xss source:local summary:taint summary:value
2 Dapper 55 55
3 JsonToItemsTaskFactory 7 7
4 Microsoft.ApplicationBlocks.Data 28 28
5 Microsoft.CSharp 24 24
6 Microsoft.EntityFrameworkCore 6 6
7 Microsoft.Extensions.Primitives Microsoft.Extensions.Caching.Distributed 54 15 54 15
8 Microsoft.VisualBasic Microsoft.Extensions.Caching.Memory 4 46 45 4 1
9 Microsoft.Extensions.Configuration 83 80 3
10 Microsoft.Extensions.DependencyInjection 62 62
11 Microsoft.Extensions.DependencyModel 12 12
12 Microsoft.Extensions.FileProviders 15 15
13 Microsoft.Extensions.FileSystemGlobbing 15 13 2
14 Microsoft.Extensions.Hosting 17 16 1
15 Microsoft.Extensions.Http 10 10
16 Microsoft.Extensions.Logging 37 37
17 Microsoft.Extensions.Options 8 8
18 Microsoft.Extensions.Primitives 63 63
19 Microsoft.Interop 27 27
20 Microsoft.NET.Build.Tasks 1 1
21 Microsoft.NETCore.Platforms.BuildTasks 4 4
22 Microsoft.VisualBasic 9 5 4
23 Microsoft.Win32 8 8
24 MySql.Data.MySqlClient 48 48
25 Newtonsoft.Json 91 73 18
26 ServiceStack 194 7 27 75 92 7
27 System 28 3 2336 12038 4 23 1 3 611 10096 1725 1942

View File

@@ -8,7 +8,7 @@ C# framework & library support
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting` Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
`ServiceStack <https://servicestack.net/>`_,"``ServiceStack.*``, ``ServiceStack``",,7,194, `ServiceStack <https://servicestack.net/>`_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
System,"``System.*``, ``System``",3,2336,28,5 System,"``System.*``, ``System``",3,12038,28,5
Others,"``Dapper``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Primitives``, ``Microsoft.VisualBasic``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,149,137, Others,"``Dapper``, ``JsonToItemsTaskFactory``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.CSharp``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NETCore.Platforms.BuildTasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,554,137,
Totals,,3,2492,359,5 Totals,,3,12599,359,5

View File

@@ -226,7 +226,8 @@ commands that you can specify for compiled languages.
- Java project built using Gradle:: - Java project built using Gradle::
codeql database create java-database --language=java --command='gradle clean test' # Use `--no-daemon` because a build delegated to an existing daemon cannot be detected by CodeQL:
codeql database create java-database --language=java --command='gradle --no-daemon clean test'
- Java project built using Maven:: - Java project built using Maven::

View File

@@ -20,10 +20,10 @@
Java,"Java 7 to 18 [4]_","javac (OpenJDK and Oracle JDK), Java,"Java 7 to 18 [4]_","javac (OpenJDK and Oracle JDK),
Eclipse compiler for Java (ECJ) [5]_",``.java`` Eclipse compiler for Java (ECJ) [5]_",``.java``
JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_" JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_"
Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10",Not applicable,``.py`` Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10",Not applicable,``.py``
Ruby [7]_,"up to 3.0.2",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``" Ruby [7]_,"up to 3.0.2",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``"
TypeScript [8]_,"2.6-4.6",Standard TypeScript compiler,"``.ts``, ``.tsx``" TypeScript [8]_,"2.6-4.7",Standard TypeScript compiler,"``.ts``, ``.tsx``, ``.mts``, ``.cts``"
.. container:: footnote-group .. container:: footnote-group

View File

@@ -4,10 +4,14 @@ import java.lang.reflect.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import com.github.codeql.Logger; import com.github.codeql.Logger;
import static com.github.codeql.ClassNamesKt.getIrDeclBinaryName; import static com.github.codeql.ClassNamesKt.getIrDeclBinaryName;
@@ -547,6 +551,51 @@ public class OdasaOutput {
(tcv.majorVersion == majorVersion && tcv.minorVersion == minorVersion && (tcv.majorVersion == majorVersion && tcv.minorVersion == minorVersion &&
tcv.lastModified < lastModified); tcv.lastModified < lastModified);
} }
private static Map<String, Map<String, Long>> jarFileEntryTimeStamps = new HashMap<>();
private static Map<String, Long> getZipFileEntryTimeStamps(String path, Logger log) {
try {
Map<String, Long> result = new HashMap<>();
ZipFile zf = new ZipFile(path);
Enumeration<? extends ZipEntry> entries = zf.entries();
while (entries.hasMoreElements()) {
ZipEntry ze = entries.nextElement();
result.put(ze.getName(), ze.getLastModifiedTime().toMillis());
}
return result;
} catch(IOException e) {
log.warn("Failed to get entry timestamps from " + path, e);
return null;
}
}
private static long getVirtualFileTimeStamp(VirtualFile vf, Logger log) {
if (vf.getFileSystem().getProtocol().equals("jar")) {
String[] parts = vf.getPath().split("!/");
if (parts.length == 2) {
String jarFilePath = parts[0];
String entryPath = parts[1];
if (!jarFileEntryTimeStamps.containsKey(jarFilePath)) {
jarFileEntryTimeStamps.put(jarFilePath, getZipFileEntryTimeStamps(jarFilePath, log));
}
Map<String, Long> entryTimeStamps = jarFileEntryTimeStamps.get(jarFilePath);
if (entryTimeStamps != null) {
Long entryTimeStamp = entryTimeStamps.get(entryPath);
if (entryTimeStamp != null)
return entryTimeStamp;
else
log.warn("Couldn't find timestamp for jar file " + jarFilePath + " entry " + entryPath);
}
} else {
log.warn("Expected JAR-file path " + vf.getPath() + " to have exactly one '!/' separator");
}
}
// For all files except for jar files, and a fallback in case of I/O problems reading a jar file:
return vf.getTimeStamp();
}
private static TrapClassVersion fromSymbol(IrDeclaration sym, Logger log) { private static TrapClassVersion fromSymbol(IrDeclaration sym, Logger log) {
VirtualFile vf = sym instanceof IrClass ? getIrClassVirtualFile((IrClass)sym) : VirtualFile vf = sym instanceof IrClass ? getIrClassVirtualFile((IrClass)sym) :
sym.getParent() instanceof IrClass ? getIrClassVirtualFile((IrClass)sym.getParent()) : sym.getParent() instanceof IrClass ? getIrClassVirtualFile((IrClass)sym.getParent()) :
@@ -583,7 +632,7 @@ public class OdasaOutput {
}; };
(new ClassReader(vf.contentsToByteArray())).accept(versionGetter, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); (new ClassReader(vf.contentsToByteArray())).accept(versionGetter, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return new TrapClassVersion(versionStore[0] & 0xffff, versionStore[0] >> 16, vf.getTimeStamp(), "kotlin"); return new TrapClassVersion(versionStore[0] & 0xffff, versionStore[0] >> 16, getVirtualFileTimeStamp(vf, log), "kotlin");
} }
catch(IllegalAccessException e) { catch(IllegalAccessException e) {
log.warn("Failed to read class file version information", e); log.warn("Failed to read class file version information", e);

View File

@@ -135,7 +135,7 @@ open class KotlinFileExtractor(
Unit Unit
} }
is IrField -> { is IrField -> {
val parentId = useDeclarationParent(declaration.parent, false)?.cast<DbReftype>() val parentId = useDeclarationParent(getFieldParent(declaration), false)?.cast<DbReftype>()
if (parentId != null) { if (parentId != null) {
extractField(declaration, parentId) extractField(declaration, parentId)
} }
@@ -759,7 +759,8 @@ open class KotlinFileExtractor(
with("field", f) { with("field", f) {
DeclarationStackAdjuster(f).use { DeclarationStackAdjuster(f).use {
declarationStack.push(f) declarationStack.push(f)
return extractField(useField(f), f.name.asString(), f.type, parentId, tw.getLocation(f), f.visibility, f, isExternalDeclaration(f), f.isFinal) val fNameSuffix = getExtensionReceiverType(f)?.let { it.classFqName?.asString()?.replace(".", "$$") } ?: ""
return extractField(useField(f), "${f.name.asString()}$fNameSuffix", f.type, parentId, tw.getLocation(f), f.visibility, f, isExternalDeclaration(f), f.isFinal)
} }
} }
} }
@@ -829,10 +830,13 @@ open class KotlinFileExtractor(
} }
if (bf != null && extractBackingField) { if (bf != null && extractBackingField) {
val fieldId = extractField(bf, parentId) val fieldParentId = useDeclarationParent(getFieldParent(bf), false)
tw.writeKtPropertyBackingFields(id, fieldId) if (fieldParentId != null) {
if (p.isDelegated) { val fieldId = extractField(bf, fieldParentId.cast())
tw.writeKtPropertyDelegates(id, fieldId) tw.writeKtPropertyBackingFields(id, fieldId)
if (p.isDelegated) {
tw.writeKtPropertyDelegates(id, fieldId)
}
} }
} }

View File

@@ -6,6 +6,7 @@ import com.semmle.extractor.java.OdasaOutput
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.ir.allOverridden import org.jetbrains.kotlin.backend.common.ir.allOverridden
import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf
import org.jetbrains.kotlin.backend.jvm.ir.getJvmNameFromAnnotation
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
@@ -1269,9 +1270,34 @@ open class KotlinUsesExtractor(
fun useValueParameter(vp: IrValueParameter, parent: Label<out DbCallable>?): Label<out DbParam> = fun useValueParameter(vp: IrValueParameter, parent: Label<out DbCallable>?): Label<out DbParam> =
tw.getLabelFor(getValueParameterLabel(vp, parent)) tw.getLabelFor(getValueParameterLabel(vp, parent))
fun isDirectlyExposedCompanionObjectField(f: IrField) =
f.hasAnnotation(FqName("kotlin.jvm.JvmField")) ||
f.correspondingPropertySymbol?.owner?.let {
it.isConst || it.isLateinit
} ?: false
fun getFieldParent(f: IrField) =
f.parentClassOrNull?.let {
if (it.isCompanion && isDirectlyExposedCompanionObjectField(f))
it.parent
else
null
} ?: f.parent
// Gets a field's corresponding property's extension receiver type, if any
fun getExtensionReceiverType(f: IrField) =
f.correspondingPropertySymbol?.owner?.let {
(it.getter ?: it.setter)?.extensionReceiverParameter?.type
}
fun getFieldLabel(f: IrField): String { fun getFieldLabel(f: IrField): String {
val parentId = useDeclarationParent(f.parent, false) val parentId = useDeclarationParent(getFieldParent(f), false)
return "@\"field;{$parentId};${f.name.asString()}\"" // Distinguish backing fields of properties based on their extension receiver type;
// otherwise two extension properties declared in the same enclosing context will get
// clashing trap labels. These are always private, so we can just make up a label without
// worrying about their names as seen from Java.
val extensionPropertyDiscriminator = getExtensionReceiverType(f)?.let { "extension;${useType(it)}" } ?: ""
return "@\"field;{$parentId};${extensionPropertyDiscriminator}${f.name.asString()}\""
} }
fun useField(f: IrField): Label<out DbField> = fun useField(f: IrField): Label<out DbField> =

View File

@@ -1,7 +1,9 @@
/** /**
* @name Capture sink models. * @name Capture sink models.
* @description Finds public methods that act as sinks as they flow into a a known sink. * @description Finds public methods that act as sinks as they flow into a a known sink.
* @kind diagnostic
* @id java/utils/model-generator/sink-models * @id java/utils/model-generator/sink-models
* @tags model-generator
*/ */
private import internal.CaptureModels private import internal.CaptureModels

View File

@@ -1,7 +1,9 @@
/** /**
* @name Capture source models. * @name Capture source models.
* @description Finds APIs that act as sources as they expose already known sources. * @description Finds APIs that act as sources as they expose already known sources.
* @id java/utils/model-generator/sink-models * @kind diagnostic
* @id java/utils/model-generator/source-models
* @tags model-generator
*/ */
private import internal.CaptureModels private import internal.CaptureModels

View File

@@ -1,7 +1,9 @@
/** /**
* @name Capture summary models. * @name Capture summary models.
* @description Finds applicable summary models to be used by other queries. * @description Finds applicable summary models to be used by other queries.
* @kind diagnostic
* @id java/utils/model-generator/summary-models * @id java/utils/model-generator/summary-models
* @tags model-generator
*/ */
private import internal.CaptureModels private import internal.CaptureModels

View File

@@ -0,0 +1,9 @@
| |
| <clinit> |
| A |
| B |
| get |
| getX |
| invoke |
| x$delegatepackagename$$subpackagename$$A |
| x$delegatepackagename$$subpackagename$$B |

View File

@@ -0,0 +1,7 @@
package packagename.subpackagename
public class A { }
public class B { }
val A.x : String by lazy { "HelloA" }
val B.x : String by lazy { "HelloB" }

View File

@@ -0,0 +1,5 @@
import java
from Class c
where c.fromSource()
select c.getAMember().toString()

View File

@@ -31,7 +31,7 @@ delegatedProperties.kt:
# 87| 0: [MethodAccess] getValue(...) # 87| 0: [MethodAccess] getValue(...)
# 87| -2: [TypeAccess] Integer # 87| -2: [TypeAccess] Integer
# 87| -1: [TypeAccess] PropertyReferenceDelegatesKt # 87| -1: [TypeAccess] PropertyReferenceDelegatesKt
# 87| 0: [VarAccess] DelegatedPropertiesKt.extDelegated$delegate # 87| 0: [VarAccess] DelegatedPropertiesKt.extDelegated$delegateMyClass
# 87| -1: [TypeAccess] DelegatedPropertiesKt # 87| -1: [TypeAccess] DelegatedPropertiesKt
# 1| 1: [ExtensionReceiverAccess] this # 1| 1: [ExtensionReceiverAccess] this
# 87| 2: [PropertyRefExpr] ...::... # 87| 2: [PropertyRefExpr] ...::...
@@ -80,7 +80,7 @@ delegatedProperties.kt:
# 87| 0: [MethodAccess] setValue(...) # 87| 0: [MethodAccess] setValue(...)
# 87| -2: [TypeAccess] Integer # 87| -2: [TypeAccess] Integer
# 87| -1: [TypeAccess] PropertyReferenceDelegatesKt # 87| -1: [TypeAccess] PropertyReferenceDelegatesKt
# 87| 0: [VarAccess] DelegatedPropertiesKt.extDelegated$delegate # 87| 0: [VarAccess] DelegatedPropertiesKt.extDelegated$delegateMyClass
# 87| -1: [TypeAccess] DelegatedPropertiesKt # 87| -1: [TypeAccess] DelegatedPropertiesKt
# 1| 1: [ExtensionReceiverAccess] this # 1| 1: [ExtensionReceiverAccess] this
# 87| 2: [PropertyRefExpr] ...::... # 87| 2: [PropertyRefExpr] ...::...
@@ -118,7 +118,7 @@ delegatedProperties.kt:
# 87| 0: [TypeAccess] MyClass # 87| 0: [TypeAccess] MyClass
# 87| 1: [TypeAccess] Integer # 87| 1: [TypeAccess] Integer
# 87| 3: [VarAccess] <set-?> # 87| 3: [VarAccess] <set-?>
# 87| 5: [FieldDeclaration] KMutableProperty0<Integer> extDelegated$delegate; # 87| 5: [FieldDeclaration] KMutableProperty0<Integer> extDelegated$delegateMyClass;
# 87| -1: [TypeAccess] KMutableProperty0<Integer> # 87| -1: [TypeAccess] KMutableProperty0<Integer>
# 87| 0: [TypeAccess] Integer # 87| 0: [TypeAccess] Integer
# 87| 0: [PropertyRefExpr] ...::... # 87| 0: [PropertyRefExpr] ...::...

View File

@@ -16,7 +16,7 @@ delegatedProperties
| delegatedProperties.kt:77:5:77:49 | delegatedToTopLevel | delegatedToTopLevel | non-local | delegatedProperties.kt:77:34:77:49 | delegatedToTopLevel$delegate | delegatedProperties.kt:77:37:77:49 | ...::... | | delegatedProperties.kt:77:5:77:49 | delegatedToTopLevel | delegatedToTopLevel | non-local | delegatedProperties.kt:77:34:77:49 | delegatedToTopLevel$delegate | delegatedProperties.kt:77:37:77:49 | ...::... |
| delegatedProperties.kt:79:5:79:38 | max | max | non-local | delegatedProperties.kt:79:18:79:38 | max$delegate | delegatedProperties.kt:79:21:79:38 | ...::... | | delegatedProperties.kt:79:5:79:38 | max | max | non-local | delegatedProperties.kt:79:18:79:38 | max$delegate | delegatedProperties.kt:79:21:79:38 | ...::... |
| delegatedProperties.kt:82:9:82:54 | delegatedToMember3 | delegatedToMember3 | local | delegatedProperties.kt:82:37:82:54 | KMutableProperty0<Integer> delegatedToMember3$delegate | delegatedProperties.kt:82:40:82:54 | ...::... | | delegatedProperties.kt:82:9:82:54 | delegatedToMember3 | delegatedToMember3 | local | delegatedProperties.kt:82:37:82:54 | KMutableProperty0<Integer> delegatedToMember3$delegate | delegatedProperties.kt:82:40:82:54 | ...::... |
| delegatedProperties.kt:87:1:87:46 | extDelegated | extDelegated | non-local | delegatedProperties.kt:87:31:87:46 | extDelegated$delegate | delegatedProperties.kt:87:34:87:46 | ...::... | | delegatedProperties.kt:87:1:87:46 | extDelegated | extDelegated | non-local | delegatedProperties.kt:87:31:87:46 | extDelegated$delegateMyClass | delegatedProperties.kt:87:34:87:46 | ...::... |
delegatedPropertyTypes delegatedPropertyTypes
| delegatedProperties.kt:6:9:9:9 | prop1 | file://:0:0:0:0 | int | file://<external>/Lazy.class:0:0:0:0 | Lazy<Integer> | | delegatedProperties.kt:6:9:9:9 | prop1 | file://:0:0:0:0 | int | file://<external>/Lazy.class:0:0:0:0 | Lazy<Integer> |
| delegatedProperties.kt:19:9:19:51 | varResource1 | file://:0:0:0:0 | int | delegatedProperties.kt:45:1:51:1 | ResourceDelegate | | delegatedProperties.kt:19:9:19:51 | varResource1 | file://:0:0:0:0 | int | delegatedProperties.kt:45:1:51:1 | ResourceDelegate |

View File

@@ -830,9 +830,9 @@
| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt | delegatedProperties.kt:87:31:87:46 | set | TypeAccess | | delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt | delegatedProperties.kt:87:31:87:46 | set | TypeAccess |
| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt | delegatedProperties.kt:87:31:87:46 | set | TypeAccess | | delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt | delegatedProperties.kt:87:31:87:46 | set | TypeAccess |
| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt | delegatedProperties.kt:87:31:87:46 | setExtDelegated | TypeAccess | | delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt | delegatedProperties.kt:87:31:87:46 | setExtDelegated | TypeAccess |
| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegate | delegatedProperties.kt:0:0:0:0 | <clinit> | VarAccess | | delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegateMyClass | delegatedProperties.kt:0:0:0:0 | <clinit> | VarAccess |
| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegate | delegatedProperties.kt:87:31:87:46 | getExtDelegated | VarAccess | | delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegateMyClass | delegatedProperties.kt:87:31:87:46 | getExtDelegated | VarAccess |
| delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegate | delegatedProperties.kt:87:31:87:46 | setExtDelegated | VarAccess | | delegatedProperties.kt:87:31:87:46 | DelegatedPropertiesKt.extDelegated$delegateMyClass | delegatedProperties.kt:87:31:87:46 | setExtDelegated | VarAccess |
| delegatedProperties.kt:87:31:87:46 | Integer | delegatedProperties.kt:87:31:87:46 | getExtDelegated | TypeAccess | | delegatedProperties.kt:87:31:87:46 | Integer | delegatedProperties.kt:87:31:87:46 | getExtDelegated | TypeAccess |
| delegatedProperties.kt:87:31:87:46 | Integer | delegatedProperties.kt:87:31:87:46 | setExtDelegated | TypeAccess | | delegatedProperties.kt:87:31:87:46 | Integer | delegatedProperties.kt:87:31:87:46 | setExtDelegated | TypeAccess |
| delegatedProperties.kt:87:31:87:46 | Integer | file://:0:0:0:0 | <none> | TypeAccess | | delegatedProperties.kt:87:31:87:46 | Integer | file://:0:0:0:0 | <none> | TypeAccess |

View File

@@ -2,7 +2,7 @@
"name": "typescript-parser-wrapper", "name": "typescript-parser-wrapper",
"private": true, "private": true,
"dependencies": { "dependencies": {
"typescript": "4.6.2" "typescript": "4.7.2"
}, },
"scripts": { "scripts": {
"build": "tsc --project tsconfig.json", "build": "tsc --project tsconfig.json",

View File

@@ -6,7 +6,7 @@
version "12.7.11" version "12.7.11"
resolved node-12.7.11.tgz#be879b52031cfb5d295b047f5462d8ef1a716446 resolved node-12.7.11.tgz#be879b52031cfb5d295b047f5462d8ef1a716446
typescript@4.6.2: typescript@4.7.2:
version "4.6.2" version "4.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.2.tgz#1f9aa2ceb9af87cca227813b4310fff0b51593c4"
integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== integrity sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==

View File

@@ -141,8 +141,9 @@ public class Fetcher {
entryPath = entryPath.subpath(1, entryPath.getNameCount()); entryPath = entryPath.subpath(1, entryPath.getNameCount());
String filename = entryPath.getFileName().toString(); String filename = entryPath.getFileName().toString();
if (!filename.endsWith(".d.ts") && !filename.equals("package.json")) { if (!filename.endsWith(".d.ts") && !filename.endsWith(".d.mts") && !filename.endsWith(".d.cts")
continue; // Only extract .d.ts files and package.json && !filename.equals("package.json")) {
continue; // Only extract .d.ts, .d.mts, .d.cts files, and package.json
} }
relativePaths.add(entryPath); relativePaths.add(entryPath);
Path outputFile = destDir.resolve(entryPath); Path outputFile = destDir.resolve(entryPath);

View File

@@ -203,7 +203,7 @@ public class FileExtractor {
} }
}, },
TYPESCRIPT(".ts", ".tsx") { TYPESCRIPT(".ts", ".tsx", ".mts", ".cts") {
@Override @Override
protected boolean contains(File f, String lcExt, ExtractorConfig config) { protected boolean contains(File f, String lcExt, ExtractorConfig config) {
if (config.getTypeScriptMode() == TypeScriptMode.NONE) return false; if (config.getTypeScriptMode() == TypeScriptMode.NONE) return false;

View File

@@ -43,7 +43,7 @@ public class Main {
* A version identifier that should be updated every time the extractor changes in such a way that * 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}. * it may produce different tuples for the same file under the same {@link ExtractorConfig}.
*/ */
public static final String EXTRACTOR_VERSION = "2022-02-22"; public static final String EXTRACTOR_VERSION = "2022-05-24";
public static final Pattern NEWLINE = Pattern.compile("\n"); public static final Pattern NEWLINE = Pattern.compile("\n");

View File

@@ -144,9 +144,9 @@ private module AccessPaths {
not param = base.getReceiver() not param = base.getReceiver()
| |
result = param and result = param and
name = param.getAnImmediateUse().asExpr().(Parameter).getName() name = param.asSource().asExpr().(Parameter).getName()
or or
param.getAnImmediateUse().asExpr() instanceof DestructuringPattern and param.asSource().asExpr() instanceof DestructuringPattern and
result = param.getMember(name) result = param.getMember(name)
) )
} }

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-experimental-atm-lib name: codeql/javascript-experimental-atm-lib
version: 0.2.1 version: 0.3.1
extractor: javascript extractor: javascript
library: true library: true
groups: groups:

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-experimental-atm-model name: codeql/javascript-experimental-atm-model
version: 0.1.1 version: 0.2.1
groups: groups:
- javascript - javascript
- experimental - experimental

View File

@@ -1,6 +1,6 @@
--- ---
dependencies: dependencies:
codeql/javascript-experimental-atm-model: codeql/javascript-experimental-atm-model:
version: 0.1.0 version: 0.2.0
compiled: false compiled: false
lockVersion: 1.0.0 lockVersion: 1.0.0

View File

@@ -6,4 +6,4 @@ groups:
- experimental - experimental
dependencies: dependencies:
codeql/javascript-experimental-atm-lib: "*" codeql/javascript-experimental-atm-lib: "*"
codeql/javascript-experimental-atm-model: "0.1.0" codeql/javascript-experimental-atm-model: "0.2.0"

View File

@@ -1,6 +1,6 @@
--- ---
dependencies: dependencies:
codeql/javascript-experimental-atm-model: codeql/javascript-experimental-atm-model:
version: 0.1.0 version: 0.2.0
compiled: false compiled: false
lockVersion: 1.0.0 lockVersion: 1.0.0

View File

@@ -1,6 +1,6 @@
name: codeql/javascript-experimental-atm-queries name: codeql/javascript-experimental-atm-queries
language: javascript language: javascript
version: 0.2.1 version: 0.3.1
suites: codeql-suites suites: codeql-suites
defaultSuiteFile: codeql-suites/javascript-atm-code-scanning.qls defaultSuiteFile: codeql-suites/javascript-atm-code-scanning.qls
groups: groups:
@@ -8,4 +8,4 @@ groups:
- experimental - experimental
dependencies: dependencies:
codeql/javascript-experimental-atm-lib: "*" codeql/javascript-experimental-atm-lib: "*"
codeql/javascript-experimental-atm-model: "0.1.0" codeql/javascript-experimental-atm-model: "0.2.0"

View File

@@ -1,6 +1,6 @@
--- ---
dependencies: dependencies:
codeql/javascript-experimental-atm-model: codeql/javascript-experimental-atm-model:
version: 0.1.0 version: 0.2.0
compiled: false compiled: false
lockVersion: 1.0.0 lockVersion: 1.0.0

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* All new ECMAScript 2022 features are now supported.

View File

@@ -0,0 +1,4 @@
---
category: majorAnalysis
---
* Added support for TypeScript 4.7.

View File

@@ -2,11 +2,7 @@
* Provides an implementation of _API graphs_, which are an abstract representation of the API * Provides an implementation of _API graphs_, which are an abstract representation of the API
* surface used and/or defined by a code base. * surface used and/or defined by a code base.
* *
* The nodes of the API graph represent definitions and uses of API components. The edges are * See `API::Node` for more in-depth documentation.
* directed and labeled; they specify how the components represented by nodes relate to each other.
* For example, if one of the nodes represents a definition of an API function, then there
* will be nodes corresponding to the function's parameters, which are connected to the function
* node by edges labeled `parameter <i>`.
*/ */
import javascript import javascript
@@ -14,50 +10,159 @@ private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
private import internal.CachedStages private import internal.CachedStages
/** /**
* Provides classes and predicates for working with APIs defined or used in a database. * Provides classes and predicates for working with the API boundary between the current
* codebase and external libraries.
*
* See `API::Node` for more in-depth documentation.
*/ */
module API { module API {
/** /**
* An abstract representation of a definition or use of an API component such as a function * A node in the API graph, representing a value that has crossed the boundary between this
* exported by an npm package, a parameter of such a function, or its result. * codebase and an external library (or in general, any external codebase).
*
* ### Basic usage
*
* API graphs are typically used to identify "API calls", that is, calls to an external function
* whose implementation is not necessarily part of the current codebase.
*
* The most basic use of API graphs is typically as follows:
* 1. Start with `API::moduleImport` for the relevant library.
* 2. Follow up with a chain of accessors such as `getMember` describing how to get to the relevant API function.
* 3. Map the resulting API graph nodes to data-flow nodes, using `asSource` or `asSink`.
*
* For example, a simplified way to get arguments to `underscore.extend` would be
* ```ql
* API::moduleImport("underscore").getMember("extend").getParameter(0).asSink()
* ```
*
* The most commonly used accessors are `getMember`, `getParameter`, and `getReturn`.
*
* ### API graph nodes
*
* There are two kinds of nodes in the API graphs, distinguished by who is "holding" the value:
* - **Use-nodes** represent values held by the current codebase, which came from an external library.
* (The current codebase is "using" a value that came from the library).
* - **Def-nodes** represent values held by the external library, which came from this codebase.
* (The current codebase "defines" the value seen by the library).
*
* API graph nodes are associated with data-flow nodes in the current codebase.
* (Since external libraries are not part of the database, there is no way to associate with concrete
* data-flow nodes from the external library).
* - **Use-nodes** are associated with data-flow nodes where a value enters the current codebase,
* such as the return value of a call to an external function.
* - **Def-nodes** are associated with data-flow nodes where a value leaves the current codebase,
* such as an argument passed in a call to an external function.
*
*
* ### Access paths and edge labels
*
* Nodes in the API graph are associated with a set of access paths, describing a series of operations
* that may be performed to obtain that value.
*
* For example, the access path `API::moduleImport("lodash").getMember("extend")` represents the action of
* importing `lodash` and then accessing the member `extend` on the resulting object.
* It would be associated with an expression such as `require("lodash").extend`.
*
* Each edge in the graph is labelled by such an "operation". For an edge `A->B`, the type of the `A` node
* determines who is performing the operation, and the type of the `B` node determines who ends up holding
* the result:
* - An edge starting from a use-node describes what the current codebase is doing to a value that
* came from a library.
* - An edge starting from a def-node describes what the external library might do to a value that
* came from the current codebase.
* - An edge ending in a use-node means the result ends up in the current codebase (at its associated data-flow node).
* - An edge ending in a def-node means the result ends up in external code (its associated data-flow node is
* the place where it was "last seen" in the current codebase before flowing out)
*
* Because the implementation of the external library is not visible, it is not known exactly what operations
* it will perform on values that flow there. Instead, the edges starting from a def-node are operations that would
* lead to an observable effect within the current codebase; without knowing for certain if the library will actually perform
* those operations. (When constructing these edges, we assume the library is somewhat well-behaved).
*
* For example, given this snippet:
* ```js
* require('foo')(x => { doSomething(x) })
* ```
* A callback is passed to the external function `foo`. We can't know if `foo` will actually invoke this callback.
* But _if_ the library should decide to invoke the callback, then a value will flow into the current codebase via the `x` parameter.
* For that reason, an edge is generated representing the argument-passing operation that might be performed by `foo`.
* This edge is going from the def-node associated with the callback to the use-node associated with the parameter `x`.
*
* ### Thinking in operations versus code patterns
*
* Treating edges as "operations" helps avoid a pitfall in which library models become overly specific to certain code patterns.
* Consider the following two equivalent calls to `foo`:
* ```js
* const foo = require('foo');
*
* foo({
* myMethod(x) {...}
* });
*
* foo({
* get myMethod() {
* return function(x) {...}
* }
* });
* ```
* If `foo` calls `myMethod` on its first parameter, either of the `myMethod` implementations will be invoked.
* And indeed, the access path `API::moduleImport("foo").getParameter(0).getMember("myMethod").getParameter(0)` correctly
* identifies both `x` parameters.
*
* Observe how `getMember("myMethod")` behaves when the member is defined via a getter. When thinking in code patterns,
* it might seem obvious that `getMember` should have obtained a reference to the getter method itself.
* But when seeing it as an access to `myMethod` performed by the library, we can deduce that the relevant expression
* on the client side is actually the return-value of the getter.
*
* Although one may think of API graphs as a tool to find certain program elements in the codebase,
* it can lead to some situations where intuition does not match what works best in practice.
*/ */
class Node extends Impl::TApiNode { class Node extends Impl::TApiNode {
/** /**
* Gets a data-flow node corresponding to a use of the API component represented by this node. * Get a data-flow node where this value may flow after entering the current codebase.
* *
* For example, `require('fs').readFileSync` is a use of the function `readFileSync` from the * This is similar to `asSource()` but additionally includes nodes that are transitively reachable by data flow.
* `fs` module, and `require('fs').readFileSync(file)` is a use of the return of that function. * See `asSource()` for examples.
*
* This includes indirect uses found via data flow, meaning that in
* `f(obj.foo); function f(x) {};` both `obj.foo` and `x` are uses of the `foo` member from `obj`.
*
* As another example, in the assignment `exports.plusOne = (x) => x+1` the two references to
* `x` are uses of the first parameter of `plusOne`.
*/ */
pragma[inline] pragma[inline]
DataFlow::Node getAUse() { DataFlow::Node getAValueReachableFromSource() {
exists(DataFlow::SourceNode src | Impl::use(this, src) | Impl::trackUseNode(this.asSource()).flowsTo(result)
Impl::trackUseNode(src).flowsTo(result)
)
} }
/** /**
* Gets an immediate use of the API component represented by this node. * Get a data-flow node where this value enters the current codebase.
* *
* For example, `require('fs').readFileSync` is a an immediate use of the `readFileSync` member * For example:
* from the `fs` module. * ```js
* // API::moduleImport("fs").asSource()
* require('fs');
* *
* Unlike `getAUse()`, this predicate only gets the immediate references, not the indirect uses * // API::moduleImport("fs").getMember("readFile").asSource()
* found via data flow. This means that in `const x = fs.readFile` only `fs.readFile` is a reference * require('fs').readFile;
* to the `readFile` member of `fs`, neither `x` nor any node that `x` flows to is a reference to *
* this API component. * // API::moduleImport("fs").getMember("readFile").getReturn().asSource()
* require('fs').readFile();
*
* require('fs').readFile(
* filename,
* // 'y' matched by API::moduleImport("fs").getMember("readFile").getParameter(1).getParameter(0).asSource()
* y => {
* ...
* });
* ```
*/ */
DataFlow::SourceNode getAnImmediateUse() { Impl::use(this, result) } DataFlow::SourceNode asSource() { Impl::use(this, result) }
/** DEPRECATED. This predicate has been renamed to `asSource`. */
deprecated DataFlow::SourceNode getAnImmediateUse() { result = this.asSource() }
/** DEPRECATED. This predicate has been renamed to `getAValueReachableFromSource`. */
deprecated DataFlow::Node getAUse() { result = this.getAValueReachableFromSource() }
/** /**
* Gets a call to the function represented by this API component. * Gets a call to the function represented by this API component.
*/ */
CallNode getACall() { result = this.getReturn().getAnImmediateUse() } CallNode getACall() { result = this.getReturn().asSource() }
/** /**
* Gets a call to the function represented by this API component, * Gets a call to the function represented by this API component,
@@ -72,7 +177,7 @@ module API {
/** /**
* Gets a `new` call to the function represented by this API component. * Gets a `new` call to the function represented by this API component.
*/ */
NewNode getAnInstantiation() { result = this.getInstance().getAnImmediateUse() } NewNode getAnInstantiation() { result = this.getInstance().asSource() }
/** /**
* Gets an invocation (with our without `new`) to the function represented by this API component. * Gets an invocation (with our without `new`) to the function represented by this API component.
@@ -80,26 +185,38 @@ module API {
InvokeNode getAnInvocation() { result = this.getACall() or result = this.getAnInstantiation() } InvokeNode getAnInvocation() { result = this.getACall() or result = this.getAnInstantiation() }
/** /**
* Gets a data-flow node corresponding to the right-hand side of a definition of the API * Get a data-flow node where this value leaves the current codebase and flows into an
* component represented by this node. * external library (or in general, any external codebase).
* *
* For example, in the assignment `exports.plusOne = (x) => x+1`, the function expression * Concretely, this is either an argument passed to a call to external code,
* `(x) => x+1` is the right-hand side of the definition of the member `plusOne` of * or the right-hand side of a property write on an object flowing into such a call.
* the enclosing module, and the expression `x+1` is the right-had side of the definition of
* its result.
* *
* Note that for parameters, it is the arguments flowing into that parameter that count as * For example:
* right-hand sides of the definition, not the declaration of the parameter itself. * ```js
* Consequently, in `require('fs').readFileSync(file)`, `file` is the right-hand * // 'x' is matched by API::moduleImport("foo").getParameter(0).asSink()
* side of a definition of the first parameter of `readFileSync` from the `fs` module. * require('foo')(x);
*
* // 'x' is matched by API::moduleImport("foo").getParameter(0).getMember("prop").asSink()
* require('foo')({
* prop: x
* });
* ```
*/ */
DataFlow::Node getARhs() { Impl::rhs(this, result) } DataFlow::Node asSink() { Impl::rhs(this, result) }
/** /**
* Gets a data-flow node that may interprocedurally flow to the right-hand side of a definition * Get a data-flow node that transitively flows to an external library (or in general, any external codebase).
* of the API component represented by this node. *
* This is similar to `asSink()` but additionally includes nodes that transitively reach a sink by data flow.
* See `asSink()` for examples.
*/ */
DataFlow::Node getAValueReachingRhs() { result = Impl::trackDefNode(this.getARhs()) } DataFlow::Node getAValueReachingSink() { result = Impl::trackDefNode(this.asSink()) }
/** DEPRECATED. This predicate has been renamed to `asSink`. */
deprecated DataFlow::Node getARhs() { result = this.asSink() }
/** DEPRECATED. This predicate has been renamed to `getAValueReachingSink`. */
deprecated DataFlow::Node getAValueReachingRhs() { result = this.getAValueReachingSink() }
/** /**
* Gets a node representing member `m` of this API component. * Gets a node representing member `m` of this API component.
@@ -334,7 +451,7 @@ module API {
* In other words, the value of a use of `that` may flow into the right-hand side of a * In other words, the value of a use of `that` may flow into the right-hand side of a
* definition of this node. * definition of this node.
*/ */
predicate refersTo(Node that) { this.getARhs() = that.getAUse() } predicate refersTo(Node that) { this.asSink() = that.getAValueReachableFromSource() }
/** /**
* Gets the data-flow node that gives rise to this node, if any. * Gets the data-flow node that gives rise to this node, if any.
@@ -445,11 +562,17 @@ module API {
bindingset[this] bindingset[this]
EntryPoint() { any() } EntryPoint() { any() }
/** Gets a data-flow node that uses this entry point. */ /** DEPRECATED. This predicate has been renamed to `getASource`. */
abstract DataFlow::SourceNode getAUse(); deprecated DataFlow::SourceNode getAUse() { none() }
/** Gets a data-flow node that defines this entry point. */ /** DEPRECATED. This predicate has been renamed to `getASink`. */
abstract DataFlow::Node getARhs(); deprecated DataFlow::SourceNode getARhs() { none() }
/** Gets a data-flow node where a value enters the current codebase through this entry-point. */
DataFlow::SourceNode getASource() { none() }
/** Gets a data-flow node where a value leaves the current codebase through this entry-point. */
DataFlow::Node getASink() { none() }
/** Gets an API-node for this entry point. */ /** Gets an API-node for this entry point. */
API::Node getANode() { result = root().getASuccessor(Label::entryPoint(this)) } API::Node getANode() { result = root().getASuccessor(Label::entryPoint(this)) }
@@ -567,7 +690,7 @@ module API {
base = MkRoot() and base = MkRoot() and
exists(EntryPoint e | exists(EntryPoint e |
lbl = Label::entryPoint(e) and lbl = Label::entryPoint(e) and
rhs = e.getARhs() rhs = e.getASink()
) )
or or
exists(string m, string prop | exists(string m, string prop |
@@ -744,7 +867,7 @@ module API {
base = MkRoot() and base = MkRoot() and
exists(EntryPoint e | exists(EntryPoint e |
lbl = Label::entryPoint(e) and lbl = Label::entryPoint(e) and
ref = e.getAUse() ref = e.getASource()
) )
or or
// property reads // property reads
@@ -1178,8 +1301,8 @@ module API {
API::Node callee; API::Node callee;
InvokeNode() { InvokeNode() {
this = callee.getReturn().getAnImmediateUse() or this = callee.getReturn().asSource() or
this = callee.getInstance().getAnImmediateUse() or this = callee.getInstance().asSource() or
this = Impl::getAPromisifiedInvocation(callee, _, _) this = Impl::getAPromisifiedInvocation(callee, _, _)
} }
@@ -1194,7 +1317,7 @@ module API {
* Gets an API node where a RHS of the node is the `i`th argument to this call. * Gets an API node where a RHS of the node is the `i`th argument to this call.
*/ */
pragma[noinline] pragma[noinline]
private Node getAParameterCandidate(int i) { result.getARhs() = this.getArgument(i) } private Node getAParameterCandidate(int i) { result.asSink() = this.getArgument(i) }
/** Gets the API node for a parameter of this invocation. */ /** Gets the API node for a parameter of this invocation. */
Node getAParameter() { result = this.getParameter(_) } Node getAParameter() { result = this.getParameter(_) }
@@ -1205,13 +1328,13 @@ module API {
/** Gets the API node for the return value of this call. */ /** Gets the API node for the return value of this call. */
Node getReturn() { Node getReturn() {
result = callee.getReturn() and result = callee.getReturn() and
result.getAnImmediateUse() = this result.asSource() = this
} }
/** Gets the API node for the object constructed by this invocation. */ /** Gets the API node for the object constructed by this invocation. */
Node getInstance() { Node getInstance() {
result = callee.getInstance() and result = callee.getInstance() and
result.getAnImmediateUse() = this result.asSource() = this
} }
} }

View File

@@ -75,7 +75,7 @@ module ArrayTaintTracking {
succ.(DataFlow::SourceNode).getAMethodCall("splice") = call succ.(DataFlow::SourceNode).getAMethodCall("splice") = call
or or
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`. // `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
call.(DataFlow::MethodCallNode).calls(pred, ["pop", "shift", "slice", "splice"]) and call.(DataFlow::MethodCallNode).calls(pred, ["pop", "shift", "slice", "splice", "at"]) and
succ = call succ = call
or or
// `e = Array.from(x)`: if `x` is tainted, then so is `e`. // `e = Array.from(x)`: if `x` is tainted, then so is `e`.
@@ -199,13 +199,13 @@ private module ArrayDataFlow {
} }
/** /**
* A step for retrieving an element from an array using `.pop()` or `.shift()`. * A step for retrieving an element from an array using `.pop()`, `.shift()`, or `.at()`.
* E.g. `array.pop()`. * E.g. `array.pop()`.
*/ */
private class ArrayPopStep extends DataFlow::SharedFlowStep { private class ArrayPopStep extends DataFlow::SharedFlowStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) { override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
exists(DataFlow::MethodCallNode call | exists(DataFlow::MethodCallNode call |
call.getMethodName() = ["pop", "shift"] and call.getMethodName() = ["pop", "shift", "at"] and
prop = arrayElement() and prop = arrayElement() and
obj = call.getReceiver() and obj = call.getReceiver() and
element = call element = call

View File

@@ -29,7 +29,7 @@ private class PlainJsonParserCall extends JsonParserCall {
callee = callee =
DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"], DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"],
"parse") or "parse") or
callee = API::moduleImport("replicator").getInstance().getMember("decode").getAnImmediateUse() or callee = API::moduleImport("replicator").getInstance().getMember("decode").asSource() or
callee = DataFlow::moduleImport("parse-json") or callee = DataFlow::moduleImport("parse-json") or
callee = DataFlow::moduleImport("json-parse-better-errors") or callee = DataFlow::moduleImport("json-parse-better-errors") or
callee = DataFlow::moduleImport("json-safe-parse") or callee = DataFlow::moduleImport("json-safe-parse") or

View File

@@ -134,7 +134,7 @@ module JsonSchema {
.ref() .ref()
.getMember(["addSchema", "validate", "compile", "compileAsync"]) .getMember(["addSchema", "validate", "compile", "compileAsync"])
.getParameter(0) .getParameter(0)
.getARhs() .asSink()
} }
} }
} }
@@ -184,7 +184,7 @@ module JsonSchema {
override boolean getPolarity() { none() } override boolean getPolarity() { none() }
override DataFlow::Node getAValidationResultAccess(boolean polarity) { override DataFlow::Node getAValidationResultAccess(boolean polarity) {
result = this.getReturn().getMember("error").getAnImmediateUse() and result = this.getReturn().getMember("error").asSource() and
polarity = false polarity = false
} }
} }

View File

@@ -14,7 +14,7 @@ class JsonStringifyCall extends DataFlow::CallNode {
callee = callee =
DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"], DataFlow::moduleMember(["json3", "json5", "flatted", "teleport-javascript", "json-cycle"],
"stringify") or "stringify") or
callee = API::moduleImport("replicator").getInstance().getMember("encode").getAnImmediateUse() or callee = API::moduleImport("replicator").getInstance().getMember("encode").asSource() or
callee = callee =
DataFlow::moduleImport([ DataFlow::moduleImport([
"json-stringify-safe", "json-stable-stringify", "stringify-object", "json-stringify-safe", "json-stable-stringify", "stringify-object",

View File

@@ -229,10 +229,10 @@ module MembershipCandidate {
membersNode = inExpr.getRightOperand() membersNode = inExpr.getRightOperand()
) )
or or
exists(MethodCallExpr hasOwn | exists(HasOwnPropertyCall hasOwn |
this = hasOwn.getArgument(0).flow() and this = hasOwn.getProperty() and
test = hasOwn and test = hasOwn.asExpr() and
hasOwn.calls(membersNode, "hasOwnProperty") membersNode = hasOwn.getObject().asExpr()
) )
} }

View File

@@ -192,3 +192,35 @@ class StringSplitCall extends DataFlow::MethodCallNode {
bindingset[i] bindingset[i]
DataFlow::Node getASubstringRead(int i) { result = this.getAPropertyRead(i.toString()) } DataFlow::Node getASubstringRead(int i) { result = this.getAPropertyRead(i.toString()) }
} }
/**
* A call to `Object.prototype.hasOwnProperty`, `Object.hasOwn`, or a library that implements
* the same functionality.
*/
class HasOwnPropertyCall extends DataFlow::Node instanceof DataFlow::CallNode {
DataFlow::Node object;
DataFlow::Node property;
HasOwnPropertyCall() {
// Make sure we handle reflective calls since libraries love to do that.
super.getCalleeNode().getALocalSource().(DataFlow::PropRead).getPropertyName() =
"hasOwnProperty" and
object = super.getReceiver() and
property = super.getArgument(0)
or
this =
[
DataFlow::globalVarRef("Object").getAMemberCall("hasOwn"), //
DataFlow::moduleImport("has").getACall(), //
LodashUnderscore::member("has").getACall()
] and
object = super.getArgument(0) and
property = super.getArgument(1)
}
/** Gets the object whose property is being checked. */
DataFlow::Node getObject() { result = object }
/** Gets the property being checked. */
DataFlow::Node getProperty() { result = property }
}

View File

@@ -1286,6 +1286,8 @@ class ExpressionWithTypeArguments extends @expression_with_type_arguments, Expr
override ControlFlowNode getFirstControlFlowNode() { override ControlFlowNode getFirstControlFlowNode() {
result = this.getExpression().getFirstControlFlowNode() result = this.getExpression().getFirstControlFlowNode()
} }
override string getAPrimaryQlClass() { result = "ExpressionWithTypeArguments" }
} }
/** /**

View File

@@ -1027,18 +1027,16 @@ module TaintTracking {
class WhitelistContainmentCallSanitizer extends AdditionalSanitizerGuardNode, class WhitelistContainmentCallSanitizer extends AdditionalSanitizerGuardNode,
DataFlow::MethodCallNode { DataFlow::MethodCallNode {
WhitelistContainmentCallSanitizer() { WhitelistContainmentCallSanitizer() {
exists(string name | this.getMethodName() = ["contains", "has", "hasOwnProperty", "hasOwn"]
name = "contains" or
name = "has" or
name = "hasOwnProperty"
|
this.getMethodName() = name
)
} }
override predicate sanitizes(boolean outcome, Expr e) { override predicate sanitizes(boolean outcome, Expr e) {
outcome = true and exists(int propertyIndex |
e = this.getArgument(0).asExpr() if this.getMethodName() = "hasOwn" then propertyIndex = 1 else propertyIndex = 0
|
outcome = true and
e = this.getArgument(propertyIndex).asExpr()
)
} }
override predicate appliesTo(Configuration cfg) { any() } override predicate appliesTo(Configuration cfg) { any() }

View File

@@ -198,7 +198,7 @@ module Babel {
.getMember(["transform", "transformSync", "transformAsync"]) .getMember(["transform", "transformSync", "transformAsync"])
.getACall() and .getACall() and
pred = call.getArgument(0) and pred = call.getArgument(0) and
succ = [call, call.getParameter(2).getParameter(0).getAnImmediateUse()] succ = [call, call.getParameter(2).getParameter(0).asSource()]
) )
} }
} }

View File

@@ -14,7 +14,7 @@ module Cheerio {
} }
/** Gets a reference to the `cheerio` function, possibly with a loaded DOM. */ /** Gets a reference to the `cheerio` function, possibly with a loaded DOM. */
DataFlow::SourceNode cheerioRef() { result = cheerioApi().getAUse() } DataFlow::SourceNode cheerioRef() { result = cheerioApi().getAValueReachableFromSource() }
/** /**
* A creation of `cheerio` object, a collection of virtual DOM elements * A creation of `cheerio` object, a collection of virtual DOM elements

View File

@@ -39,7 +39,8 @@ module ClassValidator {
/** Holds if the given field has a decorator that sanitizes its value for the purpose of taint tracking. */ /** Holds if the given field has a decorator that sanitizes its value for the purpose of taint tracking. */
predicate isFieldSanitizedByDecorator(FieldDefinition field) { predicate isFieldSanitizedByDecorator(FieldDefinition field) {
field.getADecorator().getExpression().flow() = sanitizingDecorator().getReturn().getAUse() field.getADecorator().getExpression().flow() =
sanitizingDecorator().getReturn().getAValueReachableFromSource()
} }
pragma[noinline] pragma[noinline]

View File

@@ -265,7 +265,7 @@ module ClientRequest {
or or
responseType = this.getResponseType() and responseType = this.getResponseType() and
promise = false and promise = false and
result = this.getReturn().getPromisedError().getMember("response").getAnImmediateUse() result = this.getReturn().getPromisedError().getMember("response").asSource()
} }
} }
@@ -463,7 +463,7 @@ module ClientRequest {
*/ */
private API::Node netSocketInstantiation(DataFlow::NewNode socket) { private API::Node netSocketInstantiation(DataFlow::NewNode socket) {
result = API::moduleImport("net").getMember("Socket").getInstance() and result = API::moduleImport("net").getMember("Socket").getInstance() and
socket = result.getAnImmediateUse() socket = result.asSource()
} }
/** /**
@@ -827,7 +827,7 @@ module ClientRequest {
class ApolloClientRequest extends ClientRequest::Range, API::InvokeNode { class ApolloClientRequest extends ClientRequest::Range, API::InvokeNode {
ApolloClientRequest() { this = apolloUriCallee().getAnInvocation() } ApolloClientRequest() { this = apolloUriCallee().getAnInvocation() }
override DataFlow::Node getUrl() { result = this.getParameter(0).getMember("uri").getARhs() } override DataFlow::Node getUrl() { result = this.getParameter(0).getMember("uri").asSink() }
override DataFlow::Node getHost() { none() } override DataFlow::Node getHost() { none() }
@@ -848,10 +848,10 @@ module ClientRequest {
override DataFlow::Node getUrl() { result = this.getArgument(0) } override DataFlow::Node getUrl() { result = this.getArgument(0) }
override DataFlow::Node getHost() { result = this.getParameter(0).getMember("host").getARhs() } override DataFlow::Node getHost() { result = this.getParameter(0).getMember("host").asSink() }
override DataFlow::Node getADataNode() { override DataFlow::Node getADataNode() {
result = form.getMember("append").getACall().getParameter(1).getARhs() result = form.getMember("append").getACall().getParameter(1).asSink()
} }
} }
} }

View File

@@ -21,7 +21,7 @@ private class CredentialsFromModel extends CredentialsExpr {
string kind; string kind;
CredentialsFromModel() { CredentialsFromModel() {
this = ModelOutput::getASinkNode("credentials[" + kind + "]").getARhs().asExpr() this = ModelOutput::getASinkNode("credentials[" + kind + "]").asSink().asExpr()
} }
override string getCredentialsKind() { result = kind } override string getCredentialsKind() { result = kind }

View File

@@ -9,9 +9,7 @@ module D3 {
private class D3GlobalEntry extends API::EntryPoint { private class D3GlobalEntry extends API::EntryPoint {
D3GlobalEntry() { this = "D3GlobalEntry" } D3GlobalEntry() { this = "D3GlobalEntry" }
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("d3") } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("d3") }
override DataFlow::Node getARhs() { none() }
} }
/** Gets an API node referring to the `d3` module. */ /** Gets an API node referring to the `d3` module. */
@@ -71,18 +69,18 @@ module D3 {
D3XssSink() { D3XssSink() {
exists(API::Node htmlArg | exists(API::Node htmlArg |
htmlArg = d3Selection().getMember("html").getParameter(0) and htmlArg = d3Selection().getMember("html").getParameter(0) and
this = [htmlArg, htmlArg.getReturn()].getARhs() this = [htmlArg, htmlArg.getReturn()].asSink()
) )
} }
} }
private class D3DomValueSource extends DOM::DomValueSource::Range { private class D3DomValueSource extends DOM::DomValueSource::Range {
D3DomValueSource() { D3DomValueSource() {
this = d3Selection().getMember("each").getReceiver().getAnImmediateUse() this = d3Selection().getMember("each").getReceiver().asSource()
or or
this = d3Selection().getMember("node").getReturn().getAnImmediateUse() this = d3Selection().getMember("node").getReturn().asSource()
or or
this = d3Selection().getMember("nodes").getReturn().getUnknownMember().getAnImmediateUse() this = d3Selection().getMember("nodes").getReturn().getUnknownMember().asSource()
} }
} }

View File

@@ -56,13 +56,13 @@ module Electron {
} }
} }
private API::Node browserObject() { result.getAnImmediateUse() instanceof NewBrowserObject } private API::Node browserObject() { result.asSource() instanceof NewBrowserObject }
/** /**
* A data flow node whose value may originate from a browser object instantiation. * A data flow node whose value may originate from a browser object instantiation.
*/ */
private class BrowserObjectByFlow extends BrowserObject { private class BrowserObjectByFlow extends BrowserObject {
BrowserObjectByFlow() { browserObject().getAUse() = this } BrowserObjectByFlow() { browserObject().getAValueReachableFromSource() = this }
} }
/** /**

View File

@@ -89,7 +89,7 @@ private API::Node globbyFileNameSource() {
* A file name or an array of file names from the `globby` library. * A file name or an array of file names from the `globby` library.
*/ */
private class GlobbyFileNameSource extends FileNameSource { private class GlobbyFileNameSource extends FileNameSource {
GlobbyFileNameSource() { this = globbyFileNameSource().getAnImmediateUse() } GlobbyFileNameSource() { this = globbyFileNameSource().asSource() }
} }
/** Gets a file name or an array of file names from the `fast-glob` library. */ /** Gets a file name or an array of file names from the `fast-glob` library. */
@@ -116,7 +116,7 @@ private API::Node fastGlobFileName() {
* A file name or an array of file names from the `fast-glob` library. * A file name or an array of file names from the `fast-glob` library.
*/ */
private class FastGlobFileNameSource extends FileNameSource { private class FastGlobFileNameSource extends FileNameSource {
FastGlobFileNameSource() { this = fastGlobFileName().getAnImmediateUse() } FastGlobFileNameSource() { this = fastGlobFileName().asSource() }
} }
/** /**
@@ -200,7 +200,7 @@ private class RecursiveReadDir extends FileSystemAccess, FileNameProducer, API::
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
override DataFlow::Node getAFileName() { result = this.trackFileSource().getAnImmediateUse() } override DataFlow::Node getAFileName() { result = this.trackFileSource().asSource() }
private API::Node trackFileSource() { private API::Node trackFileSource() {
result = this.getParameter([1 .. 2]).getParameter(1) result = this.getParameter([1 .. 2]).getParameter(1)
@@ -223,7 +223,7 @@ private module JsonFile {
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
override DataFlow::Node getADataNode() { result = this.trackRead().getAnImmediateUse() } override DataFlow::Node getADataNode() { result = this.trackRead().asSource() }
private API::Node trackRead() { private API::Node trackRead() {
this.getCalleeName() = "readFile" and this.getCalleeName() = "readFile" and
@@ -272,7 +272,7 @@ private class LoadJsonFile extends FileSystemReadAccess, API::CallNode {
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
override DataFlow::Node getADataNode() { result = this.trackRead().getAnImmediateUse() } override DataFlow::Node getADataNode() { result = this.trackRead().asSource() }
private API::Node trackRead() { private API::Node trackRead() {
this.getCalleeName() = "sync" and result = this.getReturn() this.getCalleeName() = "sync" and result = this.getReturn()
@@ -310,7 +310,7 @@ private class WalkDir extends FileNameProducer, FileSystemAccess, API::CallNode
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
override DataFlow::Node getAFileName() { result = this.trackFileSource().getAnImmediateUse() } override DataFlow::Node getAFileName() { result = this.trackFileSource().asSource() }
private API::Node trackFileSource() { private API::Node trackFileSource() {
not this.getCalleeName() = ["sync", "async"] and not this.getCalleeName() = ["sync", "async"] and

View File

@@ -15,7 +15,7 @@ private class BusBoyRemoteFlow extends RemoteFlowSource {
.getMember("on") .getMember("on")
.getParameter(1) .getParameter(1)
.getAParameter() .getAParameter()
.getAnImmediateUse() .asSource()
} }
override string getSourceType() { result = "parsed user value from Busbuy" } override string getSourceType() { result = "parsed user value from Busbuy" }
@@ -49,12 +49,12 @@ private class MultipartyRemoteFlow extends RemoteFlowSource {
MultipartyRemoteFlow() { MultipartyRemoteFlow() {
exists(API::Node form | form = API::moduleImport("multiparty").getMember("Form").getInstance() | exists(API::Node form | form = API::moduleImport("multiparty").getMember("Form").getInstance() |
exists(API::CallNode parse | parse = form.getMember("parse").getACall() | exists(API::CallNode parse | parse = form.getMember("parse").getACall() |
this = parse.getParameter(1).getAParameter().getAnImmediateUse() this = parse.getParameter(1).getAParameter().asSource()
) )
or or
exists(API::CallNode on | on = form.getMember("on").getACall() | exists(API::CallNode on | on = form.getMember("on").getACall() |
on.getArgument(0).mayHaveStringValue(["part", "file", "field"]) and on.getArgument(0).mayHaveStringValue(["part", "file", "field"]) and
this = on.getParameter(1).getAParameter().getAnImmediateUse() this = on.getParameter(1).getAParameter().asSource()
) )
) )
} }

View File

@@ -8,9 +8,7 @@ module History {
private class HistoryGlobalEntry extends API::EntryPoint { private class HistoryGlobalEntry extends API::EntryPoint {
HistoryGlobalEntry() { this = "HistoryLibrary" } HistoryGlobalEntry() { this = "HistoryLibrary" }
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("HistoryLibrary") } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("HistoryLibrary") }
override DataFlow::Node getARhs() { none() }
} }
/** /**
@@ -40,11 +38,11 @@ module History {
HistoryLibraryRemoteFlow() { HistoryLibraryRemoteFlow() {
exists(API::Node loc | loc = [getBrowserHistory(), getHashHistory()].getMember("location") | exists(API::Node loc | loc = [getBrowserHistory(), getHashHistory()].getMember("location") |
this = loc.getMember("hash").getAnImmediateUse() and kind.isFragment() this = loc.getMember("hash").asSource() and kind.isFragment()
or or
this = loc.getMember("pathname").getAnImmediateUse() and kind.isPath() this = loc.getMember("pathname").asSource() and kind.isPath()
or or
this = loc.getMember("search").getAnImmediateUse() and kind.isQuery() this = loc.getMember("search").asSource() and kind.isQuery()
) )
} }

View File

@@ -19,10 +19,10 @@ private module HttpProxy {
.getACall() .getACall()
} }
override DataFlow::Node getUrl() { result = getParameter(0).getMember("target").getARhs() } override DataFlow::Node getUrl() { result = getParameter(0).getMember("target").asSink() }
override DataFlow::Node getHost() { override DataFlow::Node getHost() {
result = getParameter(0).getMember("target").getMember("host").getARhs() result = getParameter(0).getMember("target").getMember("host").asSink()
} }
override DataFlow::Node getADataNode() { none() } override DataFlow::Node getADataNode() { none() }
@@ -49,10 +49,10 @@ private module HttpProxy {
) )
} }
override DataFlow::Node getUrl() { result = getOptionsObject().getMember("target").getARhs() } override DataFlow::Node getUrl() { result = getOptionsObject().getMember("target").asSink() }
override DataFlow::Node getHost() { override DataFlow::Node getHost() {
result = getOptionsObject().getMember("target").getMember("host").getARhs() result = getOptionsObject().getMember("target").getMember("host").asSink()
} }
override DataFlow::Node getADataNode() { none() } override DataFlow::Node getADataNode() { none() }
@@ -78,8 +78,8 @@ private module HttpProxy {
ProxyListenerCallback() { ProxyListenerCallback() {
exists(API::CallNode call | exists(API::CallNode call |
call = any(CreateServerCall server).getReturn().getMember(["on", "once"]).getACall() and call = any(CreateServerCall server).getReturn().getMember(["on", "once"]).getACall() and
call.getParameter(0).getARhs().mayHaveStringValue(event) and call.getParameter(0).asSink().mayHaveStringValue(event) and
this = call.getParameter(1).getARhs().getAFunctionValue() this = call.getParameter(1).asSink().getAFunctionValue()
) )
} }

View File

@@ -16,9 +16,7 @@ private module Immutable {
private class ImmutableGlobalEntry extends API::EntryPoint { private class ImmutableGlobalEntry extends API::EntryPoint {
ImmutableGlobalEntry() { this = "ImmutableGlobalEntry" } ImmutableGlobalEntry() { this = "ImmutableGlobalEntry" }
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("Immutable") } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("Immutable") }
override DataFlow::Node getARhs() { none() }
} }
/** /**

View File

@@ -69,7 +69,7 @@ module Knex {
private class KnexDatabaseAwait extends DatabaseAccess, DataFlow::ValueNode { private class KnexDatabaseAwait extends DatabaseAccess, DataFlow::ValueNode {
KnexDatabaseAwait() { KnexDatabaseAwait() {
exists(AwaitExpr enclosingAwait | this = enclosingAwait.flow() | exists(AwaitExpr enclosingAwait | this = enclosingAwait.flow() |
enclosingAwait.getOperand() = knexObject().getAUse().asExpr() enclosingAwait.getOperand() = knexObject().getAValueReachableFromSource().asExpr()
) )
} }

View File

@@ -61,10 +61,10 @@ module LdapJS {
SearchFilter() { SearchFilter() {
options = ldapClient().getMember("search").getACall().getParameter(1) and options = ldapClient().getMember("search").getACall().getParameter(1) and
this = options.getARhs() this = options.asSink()
} }
override DataFlow::Node getInput() { result = options.getMember("filter").getARhs() } override DataFlow::Node getInput() { result = options.getMember("filter").asSink() }
override DataFlow::Node getOutput() { result = this } override DataFlow::Node getOutput() { result = this }
} }

View File

@@ -12,7 +12,7 @@ private module LiveServer {
class ServerDefinition extends HTTP::Servers::StandardServerDefinition { class ServerDefinition extends HTTP::Servers::StandardServerDefinition {
ServerDefinition() { this = DataFlow::moduleImport("live-server").asExpr() } ServerDefinition() { this = DataFlow::moduleImport("live-server").asExpr() }
API::Node getImportNode() { result.getAnImmediateUse().asExpr() = this } API::Node getImportNode() { result.asSource().asExpr() = this }
} }
/** /**
@@ -41,7 +41,7 @@ private module LiveServer {
override DataFlow::SourceNode getARouteHandler() { override DataFlow::SourceNode getARouteHandler() {
exists(DataFlow::SourceNode middleware | exists(DataFlow::SourceNode middleware |
middleware = call.getParameter(0).getMember("middleware").getAValueReachingRhs() middleware = call.getParameter(0).getMember("middleware").getAValueReachingSink()
| |
result = middleware.getAMemberCall(["push", "unshift"]).getArgument(0).getAFunctionValue() result = middleware.getAMemberCall(["push", "unshift"]).getArgument(0).getAFunctionValue()
or or

View File

@@ -35,9 +35,7 @@ private module Console {
private class ConsoleGlobalEntry extends API::EntryPoint { private class ConsoleGlobalEntry extends API::EntryPoint {
ConsoleGlobalEntry() { this = "ConsoleGlobalEntry" } ConsoleGlobalEntry() { this = "ConsoleGlobalEntry" }
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("console") } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("console") }
override DataFlow::Node getARhs() { none() }
} }
/** /**
@@ -352,7 +350,7 @@ private module Pino {
// `pino` is installed as the "log" property on the request object in `Express` and similar libraries. // `pino` is installed as the "log" property on the request object in `Express` and similar libraries.
// in `Hapi` the property is "logger". // in `Hapi` the property is "logger".
exists(HTTP::RequestExpr req, API::Node reqNode | exists(HTTP::RequestExpr req, API::Node reqNode |
reqNode.getAnImmediateUse() = req.flow().getALocalSource() and reqNode.asSource() = req.flow().getALocalSource() and
result = reqNode.getMember(["log", "logger"]) result = reqNode.getMember(["log", "logger"])
) )
} }

View File

@@ -163,14 +163,14 @@ module Markdown {
or or
call = API::moduleImport("markdown-it").getMember("Markdown").getAnInvocation() call = API::moduleImport("markdown-it").getMember("Markdown").getAnInvocation()
| |
call.getParameter(0).getMember("html").getARhs().mayHaveBooleanValue(true) and call.getParameter(0).getMember("html").asSink().mayHaveBooleanValue(true) and
result = call.getReturn() result = call.getReturn()
) )
or or
exists(API::CallNode call | exists(API::CallNode call |
call = markdownIt().getMember(["use", "set", "configure", "enable", "disable"]).getACall() and call = markdownIt().getMember(["use", "set", "configure", "enable", "disable"]).getACall() and
result = call.getReturn() and result = call.getReturn() and
not call.getParameter(0).getAValueReachingRhs() = not call.getParameter(0).getAValueReachingSink() =
DataFlow::moduleImport("markdown-it-sanitizer") DataFlow::moduleImport("markdown-it-sanitizer")
) )
} }

View File

@@ -140,11 +140,9 @@ module NestJS {
private class ValidationNodeEntry extends API::EntryPoint { private class ValidationNodeEntry extends API::EntryPoint {
ValidationNodeEntry() { this = "ValidationNodeEntry" } ValidationNodeEntry() { this = "ValidationNodeEntry" }
override DataFlow::SourceNode getAUse() { override DataFlow::SourceNode getASource() {
result.(DataFlow::ClassNode).getName() = "ValidationPipe" result.(DataFlow::ClassNode).getName() = "ValidationPipe"
} }
override DataFlow::Node getARhs() { none() }
} }
/** Gets an API node referring to the constructor of `ValidationPipe` */ /** Gets an API node referring to the constructor of `ValidationPipe` */
@@ -181,7 +179,7 @@ module NestJS {
predicate hasGlobalValidationPipe(Folder folder) { predicate hasGlobalValidationPipe(Folder folder) {
exists(DataFlow::CallNode call | exists(DataFlow::CallNode call |
call.getCalleeName() = "useGlobalPipes" and call.getCalleeName() = "useGlobalPipes" and
call.getArgument(0) = validationPipe().getInstance().getAUse() and call.getArgument(0) = validationPipe().getInstance().getAValueReachableFromSource() and
folder = call.getFile().getParentContainer() folder = call.getFile().getParentContainer()
) )
or or
@@ -193,7 +191,7 @@ module NestJS {
.getAMember() .getAMember()
.getMember("useFactory") .getMember("useFactory")
.getReturn() .getReturn()
.getARhs() = validationPipe().getInstance().getAUse() and .asSink() = validationPipe().getInstance().getAValueReachableFromSource() and
folder = decorator.getFile().getParentContainer() folder = decorator.getFile().getParentContainer()
) )
or or
@@ -204,7 +202,7 @@ module NestJS {
* Holds if `param` is affected by a pipe that sanitizes inputs. * Holds if `param` is affected by a pipe that sanitizes inputs.
*/ */
private predicate hasSanitizingPipe(NestJSRequestInput param, boolean dependsOnType) { private predicate hasSanitizingPipe(NestJSRequestInput param, boolean dependsOnType) {
param.getAPipe() = sanitizingPipe(dependsOnType).getAUse() param.getAPipe() = sanitizingPipe(dependsOnType).getAValueReachableFromSource()
or or
hasGlobalValidationPipe(param.getFile().getParentContainer()) and hasGlobalValidationPipe(param.getFile().getParentContainer()) and
dependsOnType = true dependsOnType = true
@@ -395,11 +393,11 @@ module NestJS {
/** Gets a parameter with this decorator applied. */ /** Gets a parameter with this decorator applied. */
DataFlow::ParameterNode getADecoratedParameter() { DataFlow::ParameterNode getADecoratedParameter() {
result.getADecorator() = getReturn().getReturn().getAUse() result.getADecorator() = getReturn().getReturn().getAValueReachableFromSource()
} }
/** Gets a value returned by the decorator's callback, which becomes the value of the decorated parameter. */ /** Gets a value returned by the decorator's callback, which becomes the value of the decorated parameter. */
DataFlow::Node getResult() { result = getParameter(0).getReturn().getARhs() } DataFlow::Node getResult() { result = getParameter(0).getReturn().asSink() }
} }
/** /**
@@ -427,7 +425,7 @@ module NestJS {
private class ExpressRequestSource extends Express::RequestSource { private class ExpressRequestSource extends Express::RequestSource {
ExpressRequestSource() { ExpressRequestSource() {
this.(DataFlow::ParameterNode).getADecorator() = this.(DataFlow::ParameterNode).getADecorator() =
nestjs().getMember(["Req", "Request"]).getReturn().getAnImmediateUse() nestjs().getMember(["Req", "Request"]).getReturn().asSource()
or or
this = this =
executionContext() executionContext()
@@ -435,7 +433,7 @@ module NestJS {
.getReturn() .getReturn()
.getMember("getRequest") .getMember("getRequest")
.getReturn() .getReturn()
.getAnImmediateUse() .asSource()
} }
/** /**
@@ -452,7 +450,7 @@ module NestJS {
private class ExpressResponseSource extends Express::ResponseSource { private class ExpressResponseSource extends Express::ResponseSource {
ExpressResponseSource() { ExpressResponseSource() {
this.(DataFlow::ParameterNode).getADecorator() = this.(DataFlow::ParameterNode).getADecorator() =
nestjs().getMember(["Res", "Response"]).getReturn().getAnImmediateUse() nestjs().getMember(["Res", "Response"]).getReturn().asSource()
} }
/** /**

View File

@@ -252,6 +252,6 @@ module NextJS {
.getParameter(0) .getParameter(0)
.getParameter(0) .getParameter(0)
.getMember("router") .getMember("router")
.getAnImmediateUse() .asSource()
} }
} }

View File

@@ -20,7 +20,7 @@ deprecated module NoSQL = NoSql;
* Gets a value that has been assigned to the "$where" property of an object that flows to `queryArg`. * Gets a value that has been assigned to the "$where" property of an object that flows to `queryArg`.
*/ */
private DataFlow::Node getADollarWhereProperty(API::Node queryArg) { private DataFlow::Node getADollarWhereProperty(API::Node queryArg) {
result = queryArg.getMember("$where").getARhs() result = queryArg.getMember("$where").asSink()
} }
/** /**
@@ -418,7 +418,7 @@ private module Mongoose {
param = f.getParameter(0).getParameter(1) param = f.getParameter(0).getParameter(1)
| |
exists(DataFlow::MethodCallNode pred | exists(DataFlow::MethodCallNode pred |
// limitation: look at the previous method call // limitation: look at the previous method call
Query::MethodSignatures::returnsDocumentQuery(pred.getMethodName(), asArray) and Query::MethodSignatures::returnsDocumentQuery(pred.getMethodName(), asArray) and
pred.getAMethodCall() = f.getACall() pred.getAMethodCall() = f.getACall()
) )
@@ -501,7 +501,7 @@ private module Mongoose {
Credentials() { Credentials() {
exists(string prop | exists(string prop |
this = createConnection().getParameter(3).getMember(prop).getARhs().asExpr() this = createConnection().getParameter(3).getMember(prop).asSink().asExpr()
| |
prop = "user" and kind = "user name" prop = "user" and kind = "user name"
or or
@@ -518,7 +518,7 @@ private module Mongoose {
class MongoDBQueryPart extends NoSql::Query { class MongoDBQueryPart extends NoSql::Query {
MongooseFunction f; MongooseFunction f;
MongoDBQueryPart() { this = f.getQueryArgument().getARhs().asExpr() } MongoDBQueryPart() { this = f.getQueryArgument().asSink().asExpr() }
override DataFlow::Node getACodeOperator() { override DataFlow::Node getACodeOperator() {
result = getADollarWhereProperty(f.getQueryArgument()) result = getADollarWhereProperty(f.getQueryArgument())
@@ -540,7 +540,7 @@ private module Mongoose {
override DataFlow::Node getAQueryArgument() { override DataFlow::Node getAQueryArgument() {
// NB: the complete information is not easily accessible for deeply chained calls // NB: the complete information is not easily accessible for deeply chained calls
f.getQueryArgument().getARhs() = result f.getQueryArgument().asSink() = result
} }
override DataFlow::Node getAResult() { override DataFlow::Node getAResult() {
@@ -770,7 +770,7 @@ private module Redis {
RedisKeyArgument() { RedisKeyArgument() {
exists(string method, int argIndex | exists(string method, int argIndex |
QuerySignatures::argumentIsAmbiguousKey(method, argIndex) and QuerySignatures::argumentIsAmbiguousKey(method, argIndex) and
this = redis().getMember(method).getParameter(argIndex).getARhs().asExpr() this = redis().getMember(method).getParameter(argIndex).asSink().asExpr()
) )
} }
} }

View File

@@ -739,7 +739,7 @@ module NodeJSLib {
methodName = ["execFile", "execFileSync", "spawn", "spawnSync", "fork"] methodName = ["execFile", "execFileSync", "spawn", "spawnSync", "fork"]
) and ) and
// all of the above methods take the command as their first argument // all of the above methods take the command as their first argument
result = this.getParameter(0).getARhs() result = this.getParameter(0).asSink()
} }
override DataFlow::Node getACommandArgument() { result = this.getACommandArgument(_) } override DataFlow::Node getACommandArgument() { result = this.getACommandArgument(_) }
@@ -751,7 +751,7 @@ module NodeJSLib {
override DataFlow::Node getArgumentList() { override DataFlow::Node getArgumentList() {
methodName = ["execFile", "execFileSync", "fork", "spawn", "spawnSync"] and methodName = ["execFile", "execFileSync", "fork", "spawn", "spawnSync"] and
// all of the above methods take the argument list as their second argument // all of the above methods take the argument list as their second argument
result = this.getParameter(1).getARhs() result = this.getParameter(1).asSink()
} }
override predicate isSync() { methodName.matches("%Sync") } override predicate isSync() { methodName.matches("%Sync") }
@@ -759,7 +759,7 @@ module NodeJSLib {
override DataFlow::Node getOptionsArg() { override DataFlow::Node getOptionsArg() {
not result.getALocalSource() instanceof DataFlow::FunctionNode and // looks like callback not result.getALocalSource() instanceof DataFlow::FunctionNode and // looks like callback
not result.getALocalSource() instanceof DataFlow::ArrayCreationNode and // looks like argumentlist not result.getALocalSource() instanceof DataFlow::ArrayCreationNode and // looks like argumentlist
not result = this.getParameter(0).getARhs() and not result = this.getParameter(0).asSink() and
// fork/spawn and all sync methos always has options as the last argument // fork/spawn and all sync methos always has options as the last argument
if if
methodName.matches("fork%") or methodName.matches("fork%") or
@@ -768,7 +768,7 @@ module NodeJSLib {
then result = this.getLastArgument() then result = this.getLastArgument()
else else
// the rest (exec/execFile) has the options argument as their second last. // the rest (exec/execFile) has the options argument as their second last.
result = this.getParameter(this.getNumArgument() - 2).getARhs() result = this.getParameter(this.getNumArgument() - 2).asSink()
} }
} }
@@ -1070,7 +1070,7 @@ module NodeJSLib {
*/ */
private class EventEmitterSubClass extends DataFlow::ClassNode { private class EventEmitterSubClass extends DataFlow::ClassNode {
EventEmitterSubClass() { EventEmitterSubClass() {
this.getASuperClassNode() = getAnEventEmitterImport().getAUse() or this.getASuperClassNode() = getAnEventEmitterImport().getAValueReachableFromSource() or
this.getADirectSuperClass() instanceof EventEmitterSubClass this.getADirectSuperClass() instanceof EventEmitterSubClass
} }
} }

View File

@@ -22,7 +22,7 @@ private module Prettier {
call = API::moduleImport("prettier").getMember("formatWithCursor").getACall() call = API::moduleImport("prettier").getMember("formatWithCursor").getACall()
| |
pred = call.getArgument(0) and pred = call.getArgument(0) and
succ = call.getReturn().getMember("formatted").getAnImmediateUse() succ = call.getReturn().getMember("formatted").asSource()
) )
} }
} }

View File

@@ -86,7 +86,7 @@ module Puppeteer {
this = page().getMember(["addStyleTag", "addScriptTag"]).getACall() this = page().getMember(["addStyleTag", "addScriptTag"]).getACall()
} }
override DataFlow::Node getUrl() { result = getParameter(0).getMember("url").getARhs() } override DataFlow::Node getUrl() { result = getParameter(0).getMember("url").asSink() }
override DataFlow::Node getHost() { none() } override DataFlow::Node getHost() { none() }

View File

@@ -58,10 +58,10 @@ module Redux {
*/ */
class StoreCreation extends DataFlow::SourceNode instanceof StoreCreation::Range { class StoreCreation extends DataFlow::SourceNode instanceof StoreCreation::Range {
/** Gets a reference to the store. */ /** Gets a reference to the store. */
DataFlow::SourceNode ref() { result = asApiNode().getAUse() } DataFlow::SourceNode ref() { result = asApiNode().getAValueReachableFromSource() }
/** Gets an API node that refers to this store creation. */ /** Gets an API node that refers to this store creation. */
API::Node asApiNode() { result.getAnImmediateUse() = this } API::Node asApiNode() { result.asSource() = this }
/** Gets the data flow node holding the root reducer for this store. */ /** Gets the data flow node holding the root reducer for this store. */
DataFlow::Node getReducerArg() { result = super.getReducerArg() } DataFlow::Node getReducerArg() { result = super.getReducerArg() }
@@ -94,7 +94,7 @@ module Redux {
} }
override DataFlow::Node getReducerArg() { override DataFlow::Node getReducerArg() {
result = getParameter(0).getMember("reducer").getARhs() result = getParameter(0).getMember("reducer").asSink()
} }
} }
} }
@@ -106,7 +106,7 @@ module Redux {
private API::Node rootState() { private API::Node rootState() {
result instanceof RootStateSource result instanceof RootStateSource
or or
stateStep(rootState().getAUse(), result.getAnImmediateUse()) stateStep(rootState().getAValueReachableFromSource(), result.asSource())
} }
/** /**
@@ -120,7 +120,7 @@ module Redux {
accessPath = joinAccessPaths(base, prop) accessPath = joinAccessPaths(base, prop)
) )
or or
stateStep(rootStateAccessPath(accessPath).getAUse(), result.getAnImmediateUse()) stateStep(rootStateAccessPath(accessPath).getAValueReachableFromSource(), result.asSource())
} }
/** /**
@@ -193,7 +193,7 @@ module Redux {
CombineReducers() { this = combineReducers().getACall() } CombineReducers() { this = combineReducers().getACall() }
override DataFlow::Node getStateHandlerArg(string prop) { override DataFlow::Node getStateHandlerArg(string prop) {
result = getParameter(0).getMember(prop).getARhs() result = getParameter(0).getMember(prop).asSink()
} }
} }
@@ -207,7 +207,7 @@ module Redux {
*/ */
private class NestedCombineReducers extends DelegatingReducer, DataFlow::ObjectLiteralNode { private class NestedCombineReducers extends DelegatingReducer, DataFlow::ObjectLiteralNode {
NestedCombineReducers() { NestedCombineReducers() {
this = combineReducers().getParameter(0).getAMember+().getAValueReachingRhs() this = combineReducers().getParameter(0).getAMember+().getAValueReachingSink()
} }
override DataFlow::Node getStateHandlerArg(string prop) { override DataFlow::Node getStateHandlerArg(string prop) {
@@ -235,7 +235,7 @@ module Redux {
override DataFlow::Node getActionHandlerArg(DataFlow::Node actionType) { override DataFlow::Node getActionHandlerArg(DataFlow::Node actionType) {
exists(DataFlow::PropWrite write | exists(DataFlow::PropWrite write |
result = getParameter(0).getAMember().getARhs() and result = getParameter(0).getAMember().asSink() and
write.getRhs() = result and write.getRhs() = result and
actionType = write.getPropertyNameExpr().flow() actionType = write.getPropertyNameExpr().flow()
) )
@@ -374,7 +374,7 @@ module Redux {
CreateSliceReducer() { CreateSliceReducer() {
call = API::moduleImport("@reduxjs/toolkit").getMember("createSlice").getACall() and call = API::moduleImport("@reduxjs/toolkit").getMember("createSlice").getACall() and
this = call.getReturn().getMember("reducer").getAnImmediateUse() this = call.getReturn().getMember("reducer").asSource()
} }
private API::Node getABuilderRef() { private API::Node getABuilderRef() {
@@ -385,14 +385,14 @@ module Redux {
override DataFlow::Node getActionHandlerArg(DataFlow::Node actionType) { override DataFlow::Node getActionHandlerArg(DataFlow::Node actionType) {
exists(string name | exists(string name |
result = call.getParameter(0).getMember("reducers").getMember(name).getARhs() and result = call.getParameter(0).getMember("reducers").getMember(name).asSink() and
actionType = call.getReturn().getMember("actions").getMember(name).getAnImmediateUse() actionType = call.getReturn().getMember("actions").getMember(name).asSource()
) )
or or
// Properties of 'extraReducers': // Properties of 'extraReducers':
// { extraReducers: { [action]: reducer }} // { extraReducers: { [action]: reducer }}
exists(DataFlow::PropWrite write | exists(DataFlow::PropWrite write |
result = call.getParameter(0).getMember("extraReducers").getAMember().getARhs() and result = call.getParameter(0).getMember("extraReducers").getAMember().asSink() and
write.getRhs() = result and write.getRhs() = result and
actionType = write.getPropertyNameExpr().flow() actionType = write.getPropertyNameExpr().flow()
) )
@@ -444,8 +444,8 @@ module Redux {
or or
// x -> bindActionCreators({ x, ... }) // x -> bindActionCreators({ x, ... })
exists(BindActionCreatorsCall bind, string prop | exists(BindActionCreatorsCall bind, string prop |
ref(t.continue()).flowsTo(bind.getParameter(0).getMember(prop).getARhs()) and ref(t.continue()).flowsTo(bind.getParameter(0).getMember(prop).asSink()) and
result = bind.getReturn().getMember(prop).getAnImmediateUse() result = bind.getReturn().getMember(prop).asSource()
) )
or or
// x -> combineActions(x, ...) // x -> combineActions(x, ...)
@@ -580,11 +580,11 @@ module Redux {
MultiAction() { MultiAction() {
createActions = API::moduleImport("redux-actions").getMember("createActions").getACall() and createActions = API::moduleImport("redux-actions").getMember("createActions").getACall() and
this = createActions.getReturn().getMember(name).getAnImmediateUse() this = createActions.getReturn().getMember(name).asSource()
} }
override DataFlow::FunctionNode getMiddlewareFunction(boolean async) { override DataFlow::FunctionNode getMiddlewareFunction(boolean async) {
result.flowsTo(createActions.getParameter(0).getMember(getTypeTag()).getARhs()) and result.flowsTo(createActions.getParameter(0).getMember(getTypeTag()).asSink()) and
async = false async = false
} }
@@ -614,12 +614,12 @@ module Redux {
CreateSliceAction() { CreateSliceAction() {
call = API::moduleImport("@reduxjs/toolkit").getMember("createSlice").getACall() and call = API::moduleImport("@reduxjs/toolkit").getMember("createSlice").getACall() and
this = call.getReturn().getMember("actions").getMember(actionName).getAnImmediateUse() this = call.getReturn().getMember("actions").getMember(actionName).asSource()
} }
override string getTypeTag() { override string getTypeTag() {
exists(string prefix | exists(string prefix |
call.getParameter(0).getMember("name").getARhs().mayHaveStringValue(prefix) and call.getParameter(0).getMember("name").asSink().mayHaveStringValue(prefix) and
result = prefix + "/" + actionName result = prefix + "/" + actionName
) )
} }
@@ -640,7 +640,7 @@ module Redux {
override DataFlow::FunctionNode getMiddlewareFunction(boolean async) { override DataFlow::FunctionNode getMiddlewareFunction(boolean async) {
async = true and async = true and
result = getParameter(1).getAValueReachingRhs() result = getParameter(1).getAValueReachingSink()
} }
override string getTypeTag() { getArgument(0).mayHaveStringValue(result) } override string getTypeTag() { getArgument(0).mayHaveStringValue(result) }
@@ -885,12 +885,12 @@ module Redux {
accessPath = getAffectedStateAccessPath(reducer) accessPath = getAffectedStateAccessPath(reducer)
| |
pred = function.getReturnNode() and pred = function.getReturnNode() and
succ = rootStateAccessPath(accessPath).getAnImmediateUse() succ = rootStateAccessPath(accessPath).asSource()
or or
exists(string suffix, DataFlow::SourceNode base | exists(string suffix, DataFlow::SourceNode base |
base = [function.getParameter(0), function.getReturnNode().getALocalSource()] and base = [function.getParameter(0), function.getReturnNode().getALocalSource()] and
pred = AccessPath::getAnAssignmentTo(base, suffix) and pred = AccessPath::getAnAssignmentTo(base, suffix) and
succ = rootStateAccessPath(accessPath + "." + suffix).getAnImmediateUse() succ = rootStateAccessPath(accessPath + "." + suffix).asSource()
) )
) )
or or
@@ -901,7 +901,7 @@ module Redux {
reducer.isRootStateHandler() and reducer.isRootStateHandler() and
base = [function.getParameter(0), function.getReturnNode().getALocalSource()] and base = [function.getParameter(0), function.getReturnNode().getALocalSource()] and
pred = AccessPath::getAnAssignmentTo(base, suffix) and pred = AccessPath::getAnAssignmentTo(base, suffix) and
succ = rootStateAccessPath(suffix).getAnImmediateUse() succ = rootStateAccessPath(suffix).asSource()
) )
} }
@@ -916,7 +916,7 @@ module Redux {
*/ */
private DataFlow::ObjectLiteralNode getAManuallyDispatchedValue(string actionType) { private DataFlow::ObjectLiteralNode getAManuallyDispatchedValue(string actionType) {
result.getAPropertyWrite("type").getRhs().mayHaveStringValue(actionType) and result.getAPropertyWrite("type").getRhs().mayHaveStringValue(actionType) and
result = getADispatchedValueNode().getAValueReachingRhs() result = getADispatchedValueNode().getAValueReachingSink()
} }
/** /**
@@ -994,7 +994,7 @@ module Redux {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(API::CallNode call | exists(API::CallNode call |
call = useSelector().getACall() and call = useSelector().getACall() and
pred = call.getParameter(0).getReturn().getARhs() and pred = call.getParameter(0).getReturn().asSink() and
succ = call succ = call
) )
} }
@@ -1046,19 +1046,19 @@ module Redux {
// //
// const mapDispatchToProps = { foo } // const mapDispatchToProps = { foo }
// //
result = getMapDispatchToProps().getMember(name).getARhs() result = getMapDispatchToProps().getMember(name).asSink()
or or
// //
// const mapDispatchToProps = dispatch => ( { foo } ) // const mapDispatchToProps = dispatch => ( { foo } )
// //
result = getMapDispatchToProps().getReturn().getMember(name).getARhs() result = getMapDispatchToProps().getReturn().getMember(name).asSink()
or or
// Explicitly bound by bindActionCreators: // Explicitly bound by bindActionCreators:
// //
// const mapDispatchToProps = dispatch => bindActionCreators({ foo }, dispatch); // const mapDispatchToProps = dispatch => bindActionCreators({ foo }, dispatch);
// //
exists(BindActionCreatorsCall bind | exists(BindActionCreatorsCall bind |
bind.flowsTo(getMapDispatchToProps().getReturn().getARhs()) and bind.flowsTo(getMapDispatchToProps().getReturn().asSink()) and
result = bind.getOptionArgument(0, name) result = bind.getOptionArgument(0, name)
) )
} }
@@ -1096,9 +1096,7 @@ module Redux {
private class HeuristicConnectEntryPoint extends API::EntryPoint { private class HeuristicConnectEntryPoint extends API::EntryPoint {
HeuristicConnectEntryPoint() { this = "react-redux-connect" } HeuristicConnectEntryPoint() { this = "react-redux-connect" }
override DataFlow::Node getARhs() { none() } override DataFlow::SourceNode getASource() {
override DataFlow::SourceNode getAUse() {
exists(DataFlow::CallNode call | exists(DataFlow::CallNode call |
call.getAnArgument().asExpr().(Identifier).getName() = call.getAnArgument().asExpr().(Identifier).getName() =
["mapStateToProps", "mapDispatchToProps"] and ["mapStateToProps", "mapDispatchToProps"] and
@@ -1115,12 +1113,12 @@ module Redux {
override API::Node getMapStateToProps() { override API::Node getMapStateToProps() {
result = getAParameter() and result = getAParameter() and
result.getARhs().asExpr().(Identifier).getName() = "mapStateToProps" result.asSink().asExpr().(Identifier).getName() = "mapStateToProps"
} }
override API::Node getMapDispatchToProps() { override API::Node getMapDispatchToProps() {
result = getAParameter() and result = getAParameter() and
result.getARhs().asExpr().(Identifier).getName() = "mapDispatchToProps" result.asSink().asExpr().(Identifier).getName() = "mapDispatchToProps"
} }
} }
@@ -1130,7 +1128,7 @@ module Redux {
private class StateToPropsStep extends StateStep { private class StateToPropsStep extends StateStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(ConnectCall call | exists(ConnectCall call |
pred = call.getMapStateToProps().getReturn().getARhs() and pred = call.getMapStateToProps().getReturn().asSink() and
succ = call.getReactComponent().getADirectPropsAccess() succ = call.getReactComponent().getADirectPropsAccess()
) )
} }
@@ -1205,7 +1203,7 @@ module Redux {
// Selector functions may be given as an array // Selector functions may be given as an array
exists(DataFlow::ArrayCreationNode array | exists(DataFlow::ArrayCreationNode array |
array.flowsTo(getArgument(0)) and array.flowsTo(getArgument(0)) and
result.getAUse() = array.getElement(i) result.getAValueReachableFromSource() = array.getElement(i)
) )
} }
} }
@@ -1221,13 +1219,13 @@ module Redux {
// Return value of `i`th callback flows to the `i`th parameter of the last callback. // Return value of `i`th callback flows to the `i`th parameter of the last callback.
exists(CreateSelectorCall call, int index | exists(CreateSelectorCall call, int index |
call.getNumArgument() > 1 and call.getNumArgument() > 1 and
pred = call.getSelectorFunction(index).getReturn().getARhs() and pred = call.getSelectorFunction(index).getReturn().asSink() and
succ = call.getLastParameter().getParameter(index).getAnImmediateUse() succ = call.getLastParameter().getParameter(index).asSource()
) )
or or
// The result of the last callback is the final result // The result of the last callback is the final result
exists(CreateSelectorCall call | exists(CreateSelectorCall call |
pred = call.getLastParameter().getReturn().getARhs() and pred = call.getLastParameter().getReturn().asSink() and
succ = call succ = call
) )
} }

View File

@@ -9,7 +9,7 @@ module SQL {
abstract class SqlString extends Expr { } abstract class SqlString extends Expr { }
private class SqlStringFromModel extends SqlString { private class SqlStringFromModel extends SqlString {
SqlStringFromModel() { this = ModelOutput::getASinkNode("sql-injection").getARhs().asExpr() } SqlStringFromModel() { this = ModelOutput::getASinkNode("sql-injection").asSink().asExpr() }
} }
/** /**
@@ -109,7 +109,7 @@ private module MySql {
Credentials() { Credentials() {
exists(API::Node callee, string prop | exists(API::Node callee, string prop |
callee in [createConnection(), createPool()] and callee in [createConnection(), createPool()] and
this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and this = callee.getParameter(0).getMember(prop).asSink().asExpr() and
( (
prop = "user" and kind = "user name" prop = "user" and kind = "user name"
or or
@@ -200,7 +200,7 @@ private module Postgres {
QueryString() { QueryString() {
this = any(QueryCall qc).getAQueryArgument().asExpr() this = any(QueryCall qc).getAQueryArgument().asExpr()
or or
this = API::moduleImport("pg-cursor").getParameter(0).getARhs().asExpr() this = API::moduleImport("pg-cursor").getParameter(0).asSink().asExpr()
} }
} }
@@ -210,9 +210,9 @@ private module Postgres {
Credentials() { Credentials() {
exists(string prop | exists(string prop |
this = [newClient(), newPool()].getParameter(0).getMember(prop).getARhs().asExpr() this = [newClient(), newPool()].getParameter(0).getMember(prop).asSink().asExpr()
or or
this = pgPromise().getParameter(0).getMember(prop).getARhs().asExpr() this = pgPromise().getParameter(0).getMember(prop).asSink().asExpr()
| |
prop = "user" and kind = "user name" prop = "user" and kind = "user name"
or or
@@ -383,7 +383,7 @@ private module Sqlite {
/** A call to a Sqlite query method. */ /** A call to a Sqlite query method. */
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode { private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
QueryCall() { QueryCall() {
this = getAChainingQueryCall().getAnImmediateUse() this = getAChainingQueryCall().asSource()
or or
this = database().getMember("prepare").getACall() this = database().getMember("prepare").getACall()
} }
@@ -440,7 +440,8 @@ private module MsSql {
override TaggedTemplateExpr astNode; override TaggedTemplateExpr astNode;
QueryTemplateExpr() { QueryTemplateExpr() {
mssql().getMember("query").getAUse() = DataFlow::valueNode(astNode.getTag()) mssql().getMember("query").getAValueReachableFromSource() =
DataFlow::valueNode(astNode.getTag())
} }
override DataFlow::Node getAResult() { override DataFlow::Node getAResult() {
@@ -494,7 +495,7 @@ private module MsSql {
or or
callee = mssql().getMember("ConnectionPool") callee = mssql().getMember("ConnectionPool")
) and ) and
this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and this = callee.getParameter(0).getMember(prop).asSink().asExpr() and
( (
prop = "user" and kind = "user name" prop = "user" and kind = "user name"
or or

View File

@@ -27,7 +27,7 @@ private module Snapdragon {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(string methodName, API::CallNode set, API::CallNode call, API::Node base | exists(string methodName, API::CallNode set, API::CallNode call, API::Node base |
// the handler, registered with a call to `.set`. // the handler, registered with a call to `.set`.
set = getSetCall+(base.getMember(methodName + "r")).getAnImmediateUse() and set = getSetCall+(base.getMember(methodName + "r")).asSource() and
// the snapdragon instance. The API is chaining, you can also use the instance directly. // the snapdragon instance. The API is chaining, you can also use the instance directly.
base = API::moduleImport("snapdragon").getInstance() and base = API::moduleImport("snapdragon").getInstance() and
methodName = ["parse", "compile"] and methodName = ["parse", "compile"] and
@@ -47,7 +47,7 @@ private module Snapdragon {
or or
// for compiler handlers the input is the first parameter. // for compiler handlers the input is the first parameter.
methodName = "compile" and methodName = "compile" and
succ = set.getParameter(1).getParameter(0).getAnImmediateUse() succ = set.getParameter(1).getParameter(0).asSource()
) )
) )
} }

View File

@@ -41,7 +41,7 @@ module SocketIO {
class ServerObject extends SocketIOObject { class ServerObject extends SocketIOObject {
API::Node node; API::Node node;
ServerObject() { node = newServer() and this = node.getAnImmediateUse() } ServerObject() { node = newServer() and this = node.asSource() }
/** Gets the Api node for this server. */ /** Gets the Api node for this server. */
API::Node asApiNode() { result = node } API::Node asApiNode() { result = node }
@@ -81,7 +81,7 @@ module SocketIO {
) )
} }
override DataFlow::SourceNode ref() { result = this.server().getAUse() } override DataFlow::SourceNode ref() { result = this.server().getAValueReachableFromSource() }
} }
/** A data flow node that may produce (that is, create or return) a socket.io server. */ /** A data flow node that may produce (that is, create or return) a socket.io server. */
@@ -119,7 +119,7 @@ module SocketIO {
API::Node node; API::Node node;
NamespaceBase() { NamespaceBase() {
this = node.getAnImmediateUse() and this = node.asSource() and
exists(ServerObject srv | exists(ServerObject srv |
// namespace lookup on `srv` // namespace lookup on `srv`
node = srv.asApiNode().getMember("sockets") and node = srv.asApiNode().getMember("sockets") and
@@ -158,7 +158,7 @@ module SocketIO {
) )
} }
override DataFlow::SourceNode ref() { result = this.namespace().getAUse() } override DataFlow::SourceNode ref() { result = this.namespace().getAValueReachableFromSource() }
} }
/** A data flow node that may produce a namespace object. */ /** A data flow node that may produce a namespace object. */

View File

@@ -233,7 +233,7 @@ module Templating {
/** Gets an API node that may flow to `succ` through a template instantiation. */ /** Gets an API node that may flow to `succ` through a template instantiation. */
private API::Node getTemplateInput(DataFlow::SourceNode succ) { private API::Node getTemplateInput(DataFlow::SourceNode succ) {
exists(TemplateInstantiation inst, API::Node base, string name | exists(TemplateInstantiation inst, API::Node base, string name |
base.getARhs() = inst.getTemplateParamsNode() and base.asSink() = inst.getTemplateParamsNode() and
result = base.getMember(name) and result = base.getMember(name) and
succ = succ =
inst.getTemplateFile() inst.getTemplateFile()
@@ -244,7 +244,7 @@ module Templating {
) )
or or
exists(TemplateInstantiation inst, string accessPath | exists(TemplateInstantiation inst, string accessPath |
result.getARhs() = inst.getTemplateParamForValue(accessPath) and result.asSink() = inst.getTemplateParamForValue(accessPath) and
succ = succ =
inst.getTemplateFile() inst.getTemplateFile()
.getAnImportedFile*() .getAnImportedFile*()
@@ -261,7 +261,7 @@ module Templating {
private class TemplateInputStep extends DataFlow::SharedFlowStep { private class TemplateInputStep extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
getTemplateInput(succ).getARhs() = pred getTemplateInput(succ).asSink() = pred
} }
} }
@@ -321,8 +321,8 @@ module Templating {
result = this.getStringValue() result = this.getStringValue()
or or
exists(API::Node node | exists(API::Node node |
this = node.getARhs() and this = node.asSink() and
result = node.getAValueReachingRhs().getStringValue() result = node.getAValueReachingSink().getStringValue()
) )
} }
@@ -657,11 +657,9 @@ module Templating {
private class IncludeFunctionAsEntryPoint extends API::EntryPoint { private class IncludeFunctionAsEntryPoint extends API::EntryPoint {
IncludeFunctionAsEntryPoint() { this = "IncludeFunctionAsEntryPoint" } IncludeFunctionAsEntryPoint() { this = "IncludeFunctionAsEntryPoint" }
override DataFlow::SourceNode getAUse() { override DataFlow::SourceNode getASource() {
result = any(TemplatePlaceholderTag tag).getInnerTopLevel().getAVariableUse("include") result = any(TemplatePlaceholderTag tag).getInnerTopLevel().getAVariableUse("include")
} }
override DataFlow::Node getARhs() { none() }
} }
/** /**
@@ -718,7 +716,7 @@ module Templating {
override TemplateSyntax getTemplateSyntax() { result.getAPackageName() = engine } override TemplateSyntax getTemplateSyntax() { result.getAPackageName() = engine }
override DataFlow::SourceNode getOutput() { override DataFlow::SourceNode getOutput() {
result = this.getParameter([1, 2]).getParameter(1).getAnImmediateUse() result = this.getParameter([1, 2]).getParameter(1).asSource()
or or
not exists(this.getParameter([1, 2]).getParameter(1)) and not exists(this.getParameter([1, 2]).getParameter(1)) and
result = this result = this

View File

@@ -21,7 +21,7 @@ module ParseTorrent {
node = mod().getReturn() or node = mod().getReturn() or
node = mod().getMember("remote").getParameter(1).getParameter(1) node = mod().getMember("remote").getParameter(1).getParameter(1)
) and ) and
this = node.getAnImmediateUse() this = node.asSource()
} }
/** Gets the API node for this torrent object. */ /** Gets the API node for this torrent object. */
@@ -29,7 +29,9 @@ module ParseTorrent {
} }
/** Gets a data flow node referring to a parsed torrent. */ /** Gets a data flow node referring to a parsed torrent. */
DataFlow::SourceNode parsedTorrentRef() { result = any(ParsedTorrent t).asApiNode().getAUse() } DataFlow::SourceNode parsedTorrentRef() {
result = any(ParsedTorrent t).asApiNode().getAValueReachableFromSource()
}
/** /**
* An access to user-controlled torrent information. * An access to user-controlled torrent information.
@@ -38,7 +40,7 @@ module ParseTorrent {
UserControlledTorrentInfo() { UserControlledTorrentInfo() {
exists(API::Node read | exists(API::Node read |
read = any(ParsedTorrent t).asApiNode().getAMember() and read = any(ParsedTorrent t).asApiNode().getAMember() and
this = read.getAnImmediateUse() this = read.asSource()
| |
exists(string prop | exists(string prop |
not ( not (

View File

@@ -14,9 +14,7 @@ module TrustedTypes {
private class TrustedTypesEntry extends API::EntryPoint { private class TrustedTypesEntry extends API::EntryPoint {
TrustedTypesEntry() { this = "TrustedTypesEntry" } TrustedTypesEntry() { this = "TrustedTypesEntry" }
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("trustedTypes") } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("trustedTypes") }
override DataFlow::Node getARhs() { none() }
} }
private API::Node trustedTypesObj() { result = any(TrustedTypesEntry entry).getANode() } private API::Node trustedTypesObj() { result = any(TrustedTypesEntry entry).getANode() }
@@ -38,7 +36,7 @@ module TrustedTypes {
private class PolicyInputStep extends DataFlow::SharedFlowStep { private class PolicyInputStep extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(PolicyCreation policy, string method | exists(PolicyCreation policy, string method |
pred = policy.getReturn().getMember(method).getParameter(0).getARhs() and pred = policy.getReturn().getMember(method).getParameter(0).asSink() and
succ = policy.getPolicyCallback(method).getParameter(0) succ = policy.getPolicyCallback(method).getParameter(0)
) )
} }

View File

@@ -190,7 +190,7 @@ module Querystringify {
* Gets a data flow source node for member `name` of the querystringify library. * Gets a data flow source node for member `name` of the querystringify library.
*/ */
DataFlow::SourceNode querystringifyMember(string name) { DataFlow::SourceNode querystringifyMember(string name) {
result = querystringify().getMember(name).getAnImmediateUse() result = querystringify().getMember(name).asSource()
} }
/** Gets an API node referring to the `querystringify` module. */ /** Gets an API node referring to the `querystringify` module. */

View File

@@ -9,9 +9,7 @@ module Vue {
private class GlobalVueEntryPoint extends API::EntryPoint { private class GlobalVueEntryPoint extends API::EntryPoint {
GlobalVueEntryPoint() { this = "VueEntryPoint" } GlobalVueEntryPoint() { this = "VueEntryPoint" }
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("Vue") } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("Vue") }
override DataFlow::Node getARhs() { none() }
} }
/** /**
@@ -22,9 +20,7 @@ module Vue {
private class VueExportEntryPoint extends API::EntryPoint { private class VueExportEntryPoint extends API::EntryPoint {
VueExportEntryPoint() { this = "VueExportEntryPoint" } VueExportEntryPoint() { this = "VueExportEntryPoint" }
override DataFlow::SourceNode getAUse() { none() } override DataFlow::Node getASink() {
override DataFlow::Node getARhs() {
result = any(SingleFileComponent c).getModule().getDefaultOrBulkExport() result = any(SingleFileComponent c).getModule().getDefaultOrBulkExport()
} }
} }
@@ -41,7 +37,7 @@ module Vue {
/** /**
* Gets a reference to the 'Vue' object. * Gets a reference to the 'Vue' object.
*/ */
DataFlow::SourceNode vue() { result = vueLibrary().getAnImmediateUse() } DataFlow::SourceNode vue() { result = vueLibrary().asSource() }
/** Gets an API node referring to a component or `Vue`. */ /** Gets an API node referring to a component or `Vue`. */
private API::Node component() { private API::Node component() {
@@ -176,8 +172,8 @@ module Vue {
/** Gets a component which is extended by this one. */ /** Gets a component which is extended by this one. */
Component getABaseComponent() { Component getABaseComponent() {
result.getComponentRef().getAUse() = result.getComponentRef().getAValueReachableFromSource() =
getOwnOptions().getMember(["extends", "mixins"]).getARhs() getOwnOptions().getMember(["extends", "mixins"]).asSink()
} }
/** /**
@@ -195,12 +191,12 @@ module Vue {
} }
/** /**
* DEPRECATED. Use `getOwnOptions().getARhs()`. * DEPRECATED. Use `getOwnOptions().getASink()`.
* *
* Gets the options passed to the Vue object, such as the object literal `{...}` in `new Vue{{...})` * Gets the options passed to the Vue object, such as the object literal `{...}` in `new Vue{{...})`
* or the default export of a single-file component. * or the default export of a single-file component.
*/ */
deprecated DataFlow::Node getOwnOptionsObject() { result = getOwnOptions().getARhs() } deprecated DataFlow::Node getOwnOptionsObject() { result = getOwnOptions().asSink() }
/** /**
* Gets the class implementing this Vue component, if any. * Gets the class implementing this Vue component, if any.
@@ -208,19 +204,19 @@ module Vue {
* Specifically, this is a class annotated with `@Component` which flows to the options * Specifically, this is a class annotated with `@Component` which flows to the options
* object of this Vue component. * object of this Vue component.
*/ */
ClassComponent getAsClassComponent() { result = getOwnOptions().getAValueReachingRhs() } ClassComponent getAsClassComponent() { result = getOwnOptions().getAValueReachingSink() }
/** /**
* Gets the node for option `name` for this component, not including * Gets the node for option `name` for this component, not including
* those from extended objects and mixins. * those from extended objects and mixins.
*/ */
DataFlow::Node getOwnOption(string name) { result = getOwnOptions().getMember(name).getARhs() } DataFlow::Node getOwnOption(string name) { result = getOwnOptions().getMember(name).asSink() }
/** /**
* Gets the node for option `name` for this component, including those from * Gets the node for option `name` for this component, including those from
* extended objects and mixins. * extended objects and mixins.
*/ */
DataFlow::Node getOption(string name) { result = getOptions().getMember(name).getARhs() } DataFlow::Node getOption(string name) { result = getOptions().getMember(name).asSink() }
/** /**
* Gets a source node flowing into the option `name` of this component, including those from * Gets a source node flowing into the option `name` of this component, including those from
@@ -228,7 +224,7 @@ module Vue {
*/ */
pragma[nomagic] pragma[nomagic]
DataFlow::SourceNode getOptionSource(string name) { DataFlow::SourceNode getOptionSource(string name) {
result = getOptions().getMember(name).getAValueReachingRhs() result = getOptions().getMember(name).getAValueReachingSink()
} }
/** /**
@@ -289,7 +285,7 @@ module Vue {
DataFlow::FunctionNode getWatchHandler(string propName) { DataFlow::FunctionNode getWatchHandler(string propName) {
exists(API::Node propWatch | exists(API::Node propWatch |
propWatch = getOptions().getMember("watch").getMember(propName) and propWatch = getOptions().getMember("watch").getMember(propName) and
result = [propWatch, propWatch.getMember("handler")].getAValueReachingRhs() result = [propWatch, propWatch.getMember("handler")].getAValueReachingSink()
) )
} }
@@ -322,16 +318,16 @@ module Vue {
* Gets a node for a function that will be invoked with `this` bound to this component. * Gets a node for a function that will be invoked with `this` bound to this component.
*/ */
DataFlow::FunctionNode getABoundFunction() { DataFlow::FunctionNode getABoundFunction() {
result = getOptions().getAMember+().getAValueReachingRhs() result = getOptions().getAMember+().getAValueReachingSink()
or or
result = getAsClassComponent().getAnInstanceMember() result = getAsClassComponent().getAnInstanceMember()
} }
/** Gets an API node referring to an instance of this component. */ /** Gets an API node referring to an instance of this component. */
API::Node getInstance() { result.getAnImmediateUse() = getABoundFunction().getReceiver() } API::Node getInstance() { result.asSource() = getABoundFunction().getReceiver() }
/** Gets a data flow node referring to an instance of this component. */ /** Gets a data flow node referring to an instance of this component. */
DataFlow::SourceNode getAnInstanceRef() { result = getInstance().getAnImmediateUse() } DataFlow::SourceNode getAnInstanceRef() { result = getInstance().asSource() }
pragma[noinline] pragma[noinline]
private DataFlow::PropWrite getAPropertyValueWrite(string name) { private DataFlow::PropWrite getAPropertyValueWrite(string name) {
@@ -484,14 +480,12 @@ module Vue {
private class VueFileImportEntryPoint extends API::EntryPoint { private class VueFileImportEntryPoint extends API::EntryPoint {
VueFileImportEntryPoint() { this = "VueFileImportEntryPoint" } VueFileImportEntryPoint() { this = "VueFileImportEntryPoint" }
override DataFlow::SourceNode getAUse() { override DataFlow::SourceNode getASource() {
exists(Import imprt | exists(Import imprt |
imprt.getImportedPath().resolve() instanceof VueFile and imprt.getImportedPath().resolve() instanceof VueFile and
result = imprt.getImportedModuleNode() result = imprt.getImportedModuleNode()
) )
} }
override DataFlow::Node getARhs() { none() }
} }
/** /**
@@ -533,13 +527,13 @@ module Vue {
// of the .vue file. // of the .vue file.
exists(Import imprt | exists(Import imprt |
imprt.getImportedPath().resolve() = file and imprt.getImportedPath().resolve() = file and
result.getAnImmediateUse() = imprt.getImportedModuleNode() result.asSource() = imprt.getImportedModuleNode()
) )
} }
override API::Node getOwnOptions() { override API::Node getOwnOptions() {
// Use the entry point generated by `VueExportEntryPoint` // Use the entry point generated by `VueExportEntryPoint`
result.getARhs() = getModule().getDefaultOrBulkExport() result.asSink() = getModule().getDefaultOrBulkExport()
} }
override string toString() { result = file.toString() } override string toString() { result = file.toString() }
@@ -695,7 +689,7 @@ module Vue {
t.start() and t.start() and
( (
exists(API::Node router | router = API::moduleImport("vue-router") | exists(API::Node router | router = API::moduleImport("vue-router") |
result = router.getInstance().getMember("currentRoute").getAnImmediateUse() result = router.getInstance().getMember("currentRoute").asSource()
or or
result = result =
router router
@@ -703,17 +697,12 @@ module Vue {
.getMember(["beforeEach", "beforeResolve", "afterEach"]) .getMember(["beforeEach", "beforeResolve", "afterEach"])
.getParameter(0) .getParameter(0)
.getParameter([0, 1]) .getParameter([0, 1])
.getAnImmediateUse() .asSource()
or or
result = result = router.getParameter(0).getMember("scrollBehavior").getParameter([0, 1]).asSource()
router
.getParameter(0)
.getMember("scrollBehavior")
.getParameter([0, 1])
.getAnImmediateUse()
) )
or or
result = routeConfig().getMember("beforeEnter").getParameter([0, 1]).getAnImmediateUse() result = routeConfig().getMember("beforeEnter").getParameter([0, 1]).asSource()
or or
exists(Component c | exists(Component c |
result = c.getABoundFunction().getAFunctionValue().getReceiver().getAPropertyRead("$route") result = c.getABoundFunction().getAFunctionValue().getReceiver().getAPropertyRead("$route")

View File

@@ -75,7 +75,7 @@ module Vuex {
or or
exists(API::CallNode call | exists(API::CallNode call |
call = vuex().getMember("createNamespacedHelpers").getACall() and call = vuex().getMember("createNamespacedHelpers").getACall() and
namespace = call.getParameter(0).getAValueReachingRhs().getStringValue() + "/" and namespace = call.getParameter(0).getAValueReachingSink().getStringValue() + "/" and
this = call.getReturn().getMember(helperName).getACall() this = call.getReturn().getMember(helperName).getACall()
) )
) )
@@ -88,7 +88,8 @@ module Vuex {
pragma[noinline] pragma[noinline]
string getNamespace() { string getNamespace() {
getNumArgument() = 2 and getNumArgument() = 2 and
result = appendToNamespace(namespace, getParameter(0).getAValueReachingRhs().getStringValue()) result =
appendToNamespace(namespace, getParameter(0).getAValueReachingSink().getStringValue())
or or
getNumArgument() = 1 and getNumArgument() = 1 and
result = namespace result = namespace
@@ -99,28 +100,28 @@ module Vuex {
*/ */
predicate hasMapping(string localName, string storeName) { predicate hasMapping(string localName, string storeName) {
// mapGetters('foo') // mapGetters('foo')
getLastParameter().getAValueReachingRhs().getStringValue() = localName and getLastParameter().getAValueReachingSink().getStringValue() = localName and
storeName = getNamespace() + localName storeName = getNamespace() + localName
or or
// mapGetters(['foo', 'bar']) // mapGetters(['foo', 'bar'])
getLastParameter().getUnknownMember().getAValueReachingRhs().getStringValue() = localName and getLastParameter().getUnknownMember().getAValueReachingSink().getStringValue() = localName and
storeName = getNamespace() + localName storeName = getNamespace() + localName
or or
// mapGetters({foo: 'bar'}) // mapGetters({foo: 'bar'})
storeName = storeName =
getNamespace() + getNamespace() +
getLastParameter().getMember(localName).getAValueReachingRhs().getStringValue() and getLastParameter().getMember(localName).getAValueReachingSink().getStringValue() and
localName != "*" // ignore special API graph member named "*" localName != "*" // ignore special API graph member named "*"
} }
/** Gets the Vue component in which the generated functions are installed. */ /** Gets the Vue component in which the generated functions are installed. */
Vue::Component getVueComponent() { Vue::Component getVueComponent() {
exists(DataFlow::ObjectLiteralNode obj | exists(DataFlow::ObjectLiteralNode obj |
obj.getASpreadProperty() = getReturn().getAUse() and obj.getASpreadProperty() = getReturn().getAValueReachableFromSource() and
result.getOwnOptions().getAMember().getARhs() = obj result.getOwnOptions().getAMember().asSink() = obj
) )
or or
result.getOwnOptions().getAMember().getARhs() = this result.getOwnOptions().getAMember().asSink() = this
} }
} }
@@ -146,7 +147,7 @@ module Vuex {
/** Gets a value that is returned by a getter registered with the given name. */ /** Gets a value that is returned by a getter registered with the given name. */
private DataFlow::Node getterPred(string name) { private DataFlow::Node getterPred(string name) {
exists(string prefix, string prop | exists(string prefix, string prop |
result = storeConfigObject(prefix).getMember("getters").getMember(prop).getReturn().getARhs() and result = storeConfigObject(prefix).getMember("getters").getMember(prop).getReturn().asSink() and
name = prefix + prop name = prefix + prop
) )
} }
@@ -154,12 +155,12 @@ module Vuex {
/** Gets a property access that may receive the produced by a getter of the given name. */ /** Gets a property access that may receive the produced by a getter of the given name. */
private DataFlow::Node getterSucc(string name) { private DataFlow::Node getterSucc(string name) {
exists(string prefix, string prop | exists(string prefix, string prop |
result = storeRef(prefix).getMember("getters").getMember(prop).getAnImmediateUse() and result = storeRef(prefix).getMember("getters").getMember(prop).asSource() and
prop != "*" and prop != "*" and
name = prefix + prop name = prefix + prop
) )
or or
result = getAMappedAccess("mapGetters", name).getAnImmediateUse() result = getAMappedAccess("mapGetters", name).asSource()
} }
/** Holds if `pred -> succ` is a step from a getter function to a relevant property access. */ /** Holds if `pred -> succ` is a step from a getter function to a relevant property access. */
@@ -212,19 +213,19 @@ module Vuex {
commitCall = commitLikeFunctionRef(kind, prefix).getACall() commitCall = commitLikeFunctionRef(kind, prefix).getACall()
| |
// commit('name', payload) // commit('name', payload)
name = prefix + commitCall.getParameter(0).getAValueReachingRhs().getStringValue() and name = prefix + commitCall.getParameter(0).getAValueReachingSink().getStringValue() and
result = commitCall.getArgument(1) result = commitCall.getArgument(1)
or or
// commit({type: 'name', ...<payload>...}) // commit({type: 'name', ...<payload>...})
name = name =
prefix + prefix +
commitCall.getParameter(0).getMember("type").getAValueReachingRhs().getStringValue() and commitCall.getParameter(0).getMember("type").getAValueReachingSink().getStringValue() and
result = commitCall.getArgument(0) result = commitCall.getArgument(0)
) )
or or
// this.name(payload) // this.name(payload)
// methods: {...mapMutations(['name'])} } // methods: {...mapMutations(['name'])} }
result = getAMappedAccess(getMapHelperForCommitKind(kind), name).getParameter(0).getARhs() result = getAMappedAccess(getMapHelperForCommitKind(kind), name).getParameter(0).asSink()
} }
/** Gets a node that refers the payload of a committed mutation with the given `name.` */ /** Gets a node that refers the payload of a committed mutation with the given `name.` */
@@ -238,7 +239,7 @@ module Vuex {
.getMember(getStorePropForCommitKind(kind)) .getMember(getStorePropForCommitKind(kind))
.getMember(prop) .getMember(prop)
.getParameter(1) .getParameter(1)
.getAnImmediateUse() and .asSource() and
prop != "*" and prop != "*" and
name = prefix + prop name = prefix + prop
) )
@@ -293,19 +294,17 @@ module Vuex {
/** Gets a value that flows into the given access path of the state. */ /** Gets a value that flows into the given access path of the state. */
DataFlow::Node stateMutationPred(string path) { DataFlow::Node stateMutationPred(string path) {
result = stateRefByAccessPath(path).getARhs() result = stateRefByAccessPath(path).asSink()
or or
exists(ExtendCall call, string base, string prop | exists(ExtendCall call, string base, string prop |
call.getDestinationOperand() = stateRefByAccessPath(base).getAUse() and call.getDestinationOperand() = stateRefByAccessPath(base).getAValueReachableFromSource() and
result = call.getASourceOperand().getALocalSource().getAPropertyWrite(prop).getRhs() and result = call.getASourceOperand().getALocalSource().getAPropertyWrite(prop).getRhs() and
path = appendToNamespace(base, prop) path = appendToNamespace(base, prop)
) )
} }
/** Gets a value that refers to the given access path of the state. */ /** Gets a value that refers to the given access path of the state. */
DataFlow::Node stateMutationSucc(string path) { DataFlow::Node stateMutationSucc(string path) { result = stateRefByAccessPath(path).asSource() }
result = stateRefByAccessPath(path).getAnImmediateUse()
}
/** Holds if `pred -> succ` is a step from state mutation to state access. */ /** Holds if `pred -> succ` is a step from state mutation to state access. */
predicate stateMutationStep(DataFlow::Node pred, DataFlow::Node succ) { predicate stateMutationStep(DataFlow::Node pred, DataFlow::Node succ) {
@@ -325,7 +324,7 @@ module Vuex {
exists(MapHelperCall call | exists(MapHelperCall call |
call.getHelperName() = "mapState" and call.getHelperName() = "mapState" and
component = call.getVueComponent() and component = call.getVueComponent() and
result = call.getLastParameter().getMember(name).getReturn().getARhs() result = call.getLastParameter().getMember(name).getReturn().asSink()
) )
} }
@@ -336,7 +335,7 @@ module Vuex {
predicate mapStateHelperStep(DataFlow::Node pred, DataFlow::Node succ) { predicate mapStateHelperStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(Vue::Component component, string name | exists(Vue::Component component, string name |
pred = mapStateHelperPred(component, name) and pred = mapStateHelperPred(component, name) and
succ = pragma[only_bind_out](component).getInstance().getMember(name).getAnImmediateUse() succ = pragma[only_bind_out](component).getInstance().getMember(name).asSource()
) )
} }
@@ -378,7 +377,7 @@ module Vuex {
/** Gets a package that can be considered an entry point for a Vuex app. */ /** Gets a package that can be considered an entry point for a Vuex app. */
private PackageJson entryPointPackage() { private PackageJson entryPointPackage() {
result = getPackageJson(storeRef().getAnImmediateUse().getFile()) result = getPackageJson(storeRef().asSource().getFile())
or or
// Any package that imports a store-creating package is considered a potential entry point. // Any package that imports a store-creating package is considered a potential entry point.
packageDependsOn(result, entryPointPackage()) packageDependsOn(result, entryPointPackage())

View File

@@ -100,7 +100,7 @@ module XML {
} }
override DataFlow::Node getAResult() { override DataFlow::Node getAResult() {
result = [doc(), element(), attr()].getAnImmediateUse() result = [doc(), element(), attr()].asSource()
or or
result = element().getMember(["name", "text"]).getACall() result = element().getMember(["name", "text"]).getACall()
or or
@@ -282,11 +282,7 @@ module XML {
override DataFlow::Node getAResult() { override DataFlow::Node getAResult() {
result = result =
parser parser.getReturn().getMember(any(string s | s.matches("on%"))).getAParameter().asSource()
.getReturn()
.getMember(any(string s | s.matches("on%")))
.getAParameter()
.getAnImmediateUse()
} }
} }

View File

@@ -26,7 +26,7 @@ import Shared::ModelOutput as ModelOutput
* A remote flow source originating from a CSV source row. * A remote flow source originating from a CSV source row.
*/ */
private class RemoteFlowSourceFromCsv extends RemoteFlowSource { private class RemoteFlowSourceFromCsv extends RemoteFlowSource {
RemoteFlowSourceFromCsv() { this = ModelOutput::getASourceNode("remote").getAnImmediateUse() } RemoteFlowSourceFromCsv() { this = ModelOutput::getASourceNode("remote").asSource() }
override string getSourceType() { result = "Remote flow" } override string getSourceType() { result = "Remote flow" }
} }
@@ -37,8 +37,8 @@ private class RemoteFlowSourceFromCsv extends RemoteFlowSource {
private predicate summaryStepNodes(DataFlow::Node pred, DataFlow::Node succ, string kind) { private predicate summaryStepNodes(DataFlow::Node pred, DataFlow::Node succ, string kind) {
exists(API::Node predNode, API::Node succNode | exists(API::Node predNode, API::Node succNode |
Specific::summaryStep(predNode, succNode, kind) and Specific::summaryStep(predNode, succNode, kind) and
pred = predNode.getARhs() and pred = predNode.asSink() and
succ = succNode.getAnImmediateUse() succ = succNode.asSource()
) )
} }

View File

@@ -299,7 +299,7 @@ private class AccessPathRange extends AccessPath::Range {
bindingset[token] bindingset[token]
API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) { API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
// API graphs use the same label for arguments and parameters. An edge originating from a // API graphs use the same label for arguments and parameters. An edge originating from a
// use-node represents be an argument, and an edge originating from a def-node represents a parameter. // use-node represents an argument, and an edge originating from a def-node represents a parameter.
// We just map both to the same thing. // We just map both to the same thing.
token.getName() = ["Argument", "Parameter"] and token.getName() = ["Argument", "Parameter"] and
result = node.getParameter(AccessPath::parseIntUnbounded(token.getAnArgument())) result = node.getParameter(AccessPath::parseIntUnbounded(token.getAnArgument()))

View File

@@ -61,9 +61,7 @@ private class GlobalApiEntryPoint extends API::EntryPoint {
this = "GlobalApiEntryPoint:" + global this = "GlobalApiEntryPoint:" + global
} }
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef(global) } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef(global) }
override DataFlow::Node getARhs() { none() }
/** Gets the name of the global variable. */ /** Gets the name of the global variable. */
string getGlobal() { result = global } string getGlobal() { result = global }
@@ -151,7 +149,7 @@ API::Node getExtraSuccessorFromInvoke(API::InvokeNode node, AccessPathToken toke
or or
token.getName() = "Argument" and token.getName() = "Argument" and
token.getAnArgument() = "this" and token.getAnArgument() = "this" and
result.getARhs() = node.(DataFlow::CallNode).getReceiver() result.asSink() = node.(DataFlow::CallNode).getReceiver()
} }
/** /**

View File

@@ -58,7 +58,7 @@ class RemoteServerResponse extends HeuristicSource, RemoteFlowSource {
*/ */
private class RemoteFlowSourceFromDBAccess extends RemoteFlowSource, HeuristicSource { private class RemoteFlowSourceFromDBAccess extends RemoteFlowSource, HeuristicSource {
RemoteFlowSourceFromDBAccess() { RemoteFlowSourceFromDBAccess() {
this = ModelOutput::getASourceNode("database-access-result").getAUse() or this = ModelOutput::getASourceNode("database-access-result").getAValueReachableFromSource() or
exists(DatabaseAccess dba | this = dba.getAResult()) exists(DatabaseAccess dba | this = dba.getAResult())
} }

View File

@@ -49,7 +49,7 @@ module DomBasedXss {
or or
// A construction of a JSDOM object (server side DOM), where scripts are allowed. // A construction of a JSDOM object (server side DOM), where scripts are allowed.
exists(DataFlow::NewNode instance | exists(DataFlow::NewNode instance |
instance = API::moduleImport("jsdom").getMember("JSDOM").getInstance().getAnImmediateUse() and instance = API::moduleImport("jsdom").getMember("JSDOM").getInstance().asSource() and
this = instance.getArgument(0) and this = instance.getArgument(0) and
instance.getOptionArgument(1, "runScripts").mayHaveStringValue("dangerously") instance.getOptionArgument(1, "runScripts").mayHaveStringValue("dangerously")
) )

View File

@@ -61,7 +61,7 @@ module ExceptionXss {
*/ */
private class JsonSchemaValidationError extends Source { private class JsonSchemaValidationError extends Source {
JsonSchemaValidationError() { JsonSchemaValidationError() {
this = any(JsonSchema::Ajv::Instance i).getAValidationError().getAnImmediateUse() this = any(JsonSchema::Ajv::Instance i).getAValidationError().asSource()
or or
this = any(JsonSchema::Joi::JoiValidationErrorRead r).getAValidationResultAccess(_) this = any(JsonSchema::Joi::JoiValidationErrorRead r).getAValidationResultAccess(_)
} }

View File

@@ -48,7 +48,7 @@ module ExternalApiUsedWithUntrustedData {
} }
/** Holds if `node` corresponds to a deep object argument. */ /** Holds if `node` corresponds to a deep object argument. */
private predicate isDeepObjectSink(API::Node node) { node.getARhs() instanceof DeepObjectSink } private predicate isDeepObjectSink(API::Node node) { node.asSink() instanceof DeepObjectSink }
/** /**
* A sanitizer for data flowing to an external API. * A sanitizer for data flowing to an external API.
@@ -165,9 +165,9 @@ module ExternalApiUsedWithUntrustedData {
not param = base.getReceiver() not param = base.getReceiver()
| |
result = param and result = param and
name = param.getAnImmediateUse().asExpr().(Parameter).getName() name = param.asSource().asExpr().(Parameter).getName()
or or
param.getAnImmediateUse().asExpr() instanceof DestructuringPattern and param.asSource().asExpr() instanceof DestructuringPattern and
result = param.getMember(name) result = param.getMember(name)
) )
} }

View File

@@ -74,7 +74,7 @@ module IndirectCommandInjection {
].getMember("parse").getACall() ].getMember("parse").getACall()
or or
// `require('commander').myCmdArgumentName` // `require('commander').myCmdArgumentName`
this = commander().getAMember().getAnImmediateUse() this = commander().getAMember().asSource()
or or
// `require('commander').opt()` => `{a: ..., b: ...}` // `require('commander').opt()` => `{a: ..., b: ...}`
this = commander().getMember("opts").getACall() this = commander().getMember("opts").getACall()

View File

@@ -152,9 +152,7 @@ abstract class RateLimitingMiddleware extends DataFlow::SourceNode {
* A rate limiter constructed using the `express-rate-limit` package. * A rate limiter constructed using the `express-rate-limit` package.
*/ */
class ExpressRateLimit extends RateLimitingMiddleware { class ExpressRateLimit extends RateLimitingMiddleware {
ExpressRateLimit() { ExpressRateLimit() { this = API::moduleImport("express-rate-limit").getReturn().asSource() }
this = API::moduleImport("express-rate-limit").getReturn().getAnImmediateUse()
}
} }
/** /**
@@ -162,7 +160,7 @@ class ExpressRateLimit extends RateLimitingMiddleware {
*/ */
class BruteForceRateLimit extends RateLimitingMiddleware { class BruteForceRateLimit extends RateLimitingMiddleware {
BruteForceRateLimit() { BruteForceRateLimit() {
this = API::moduleImport("express-brute").getInstance().getMember("prevent").getAnImmediateUse() this = API::moduleImport("express-brute").getInstance().getMember("prevent").asSource()
} }
} }
@@ -174,7 +172,7 @@ class BruteForceRateLimit extends RateLimitingMiddleware {
*/ */
class RouteHandlerLimitedByExpressLimiter extends RateLimitingMiddleware { class RouteHandlerLimitedByExpressLimiter extends RateLimitingMiddleware {
RouteHandlerLimitedByExpressLimiter() { RouteHandlerLimitedByExpressLimiter() {
this = API::moduleImport("express-limiter").getReturn().getReturn().getAnImmediateUse() this = API::moduleImport("express-limiter").getReturn().getReturn().asSource()
} }
override Routing::Node getRoutingNode() { override Routing::Node getRoutingNode() {
@@ -211,7 +209,7 @@ class RateLimiterFlexibleRateLimiter extends DataFlow::FunctionNode {
rateLimiterClass = API::moduleImport("rate-limiter-flexible").getMember(rateLimiterClassName) and rateLimiterClass = API::moduleImport("rate-limiter-flexible").getMember(rateLimiterClassName) and
rateLimiterConsume = rateLimiterClass.getInstance().getMember("consume") and rateLimiterConsume = rateLimiterClass.getInstance().getMember("consume") and
request.getParameter() = getRouteHandlerParameter(this.getFunction(), "request") and request.getParameter() = getRouteHandlerParameter(this.getFunction(), "request") and
request.getAPropertyRead().flowsTo(rateLimiterConsume.getAParameter().getARhs()) request.getAPropertyRead().flowsTo(rateLimiterConsume.getAParameter().asSink())
) )
} }
} }

View File

@@ -164,9 +164,7 @@ private class ExternalRemoteFlowSourceSpecEntryPoint extends API::EntryPoint {
string getName() { result = name } string getName() { result = name }
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef(name) } override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef(name) }
override DataFlow::Node getARhs() { none() }
} }
/** /**
@@ -175,7 +173,7 @@ private class ExternalRemoteFlowSourceSpecEntryPoint extends API::EntryPoint {
private class ExternalRemoteFlowSource extends RemoteFlowSource { private class ExternalRemoteFlowSource extends RemoteFlowSource {
RemoteFlowSourceAccessPath ap; RemoteFlowSourceAccessPath ap;
ExternalRemoteFlowSource() { Stages::Taint::ref() and this = ap.resolve().getAnImmediateUse() } ExternalRemoteFlowSource() { Stages::Taint::ref() and this = ap.resolve().asSource() }
override string getSourceType() { result = ap.getSourceType() } override string getSourceType() { result = ap.getSourceType() }
} }

View File

@@ -78,14 +78,8 @@ module ResourceExhaustion {
exists(DataFlow::SourceNode clazz, DataFlow::InvokeNode invk, int index | exists(DataFlow::SourceNode clazz, DataFlow::InvokeNode invk, int index |
clazz = DataFlow::globalVarRef("Buffer") and this = invk.getArgument(index) clazz = DataFlow::globalVarRef("Buffer") and this = invk.getArgument(index)
| |
exists(string name | invk = clazz.getAMemberCall(["alloc", "allocUnsafe", "allocUnsafeSlow"]) and
invk = clazz.getAMemberCall(name) and index = 0 // the buffer size
(
name = "from" and index = 2 // the length argument
or
name = ["alloc", "allocUnsafe", "allocUnsafeSlow"] and index = 0 // the buffer size
)
)
or or
invk = clazz.getAnInvocation() and invk = clazz.getAnInvocation() and
( (

View File

@@ -51,7 +51,7 @@ module SqlInjection {
this = any(LdapJS::ClientCall call).getArgument(0) this = any(LdapJS::ClientCall call).getArgument(0)
or or
// A search options object, which contains a filter and a baseDN. // A search options object, which contains a filter and a baseDN.
this = any(LdapJS::SearchOptions opt).getARhs() this = any(LdapJS::SearchOptions opt).asSink()
or or
// A call to "parseDN", which parses a DN from a string. // A call to "parseDN", which parses a DN from a string.
this = LdapJS::ldapjs().getMember("parseDN").getACall().getArgument(0) this = LdapJS::ldapjs().getMember("parseDN").getACall().getArgument(0)

View File

@@ -681,7 +681,7 @@ module TaintedPath {
.getMember(["pdf", "screenshot"]) .getMember(["pdf", "screenshot"])
.getParameter(0) .getParameter(0)
.getMember("path") .getMember("path")
.getARhs() .asSink()
} }
} }
@@ -702,7 +702,7 @@ module TaintedPath {
.getACall() .getACall()
.getParameter(1) .getParameter(1)
.getMember("config") .getMember("config")
.getARhs() .asSink()
} }
} }
@@ -716,7 +716,7 @@ module TaintedPath {
.getMember(["readPackageAsync", "readPackageSync"]) .getMember(["readPackageAsync", "readPackageSync"])
.getParameter(0) .getParameter(0)
.getMember("cwd") .getMember("cwd")
.getARhs() .asSink()
} }
} }
@@ -726,8 +726,8 @@ module TaintedPath {
private class ShellCwdSink extends TaintedPath::Sink { private class ShellCwdSink extends TaintedPath::Sink {
ShellCwdSink() { ShellCwdSink() {
exists(SystemCommandExecution sys, API::Node opts | exists(SystemCommandExecution sys, API::Node opts |
opts.getARhs() = sys.getOptionsArg() and // assuming that an API::Node exists here. opts.asSink() = sys.getOptionsArg() and // assuming that an API::Node exists here.
this = opts.getMember("cwd").getARhs() this = opts.getMember("cwd").asSink()
) )
} }
} }

View File

@@ -27,4 +27,30 @@ class Configuration extends DataFlow::Configuration {
} }
override predicate isBarrier(DataFlow::Node node) { node instanceof Barrier } override predicate isBarrier(DataFlow::Node node) { node instanceof Barrier }
override predicate isBarrierGuard(DataFlow::BarrierGuardNode guard) {
guard instanceof TypeOfTestBarrier or
guard instanceof IsArrayBarrier
}
}
private class TypeOfTestBarrier extends DataFlow::BarrierGuardNode, DataFlow::ValueNode {
override EqualityTest astNode;
TypeOfTestBarrier() { TaintTracking::isTypeofGuard(astNode, _, _) }
override predicate blocks(boolean outcome, Expr e) {
if TaintTracking::isTypeofGuard(astNode, e, ["string", "object"])
then outcome = [true, false] // separation between string/array removes type confusion in both branches
else outcome = astNode.getPolarity() // block flow to branch where value is neither string nor array
}
}
private class IsArrayBarrier extends DataFlow::BarrierGuardNode, DataFlow::CallNode {
IsArrayBarrier() { this = DataFlow::globalVarRef("Array").getAMemberCall("isArray").getACall() }
override predicate blocks(boolean outcome, Expr e) {
e = getArgument(0).asExpr() and
outcome = [true, false] // separation between string/array removes type confusion in both branches
}
} }

View File

@@ -208,8 +208,7 @@ module XssThroughDom {
exists(API::Node useForm | exists(API::Node useForm |
useForm = API::moduleImport("react-hook-form").getMember("useForm").getReturn() useForm = API::moduleImport("react-hook-form").getMember("useForm").getReturn()
| |
this = this = useForm.getMember("handleSubmit").getParameter(0).getParameter(0).asSource()
useForm.getMember("handleSubmit").getParameter(0).getParameter(0).getAnImmediateUse()
or or
this = useForm.getMember("getValues").getACall() this = useForm.getMember("getValues").getACall()
) )

View File

@@ -103,7 +103,7 @@ module ZipSlip {
class JSZipFilesSource extends Source instanceof DynamicPropertyAccess::EnumeratedPropName { class JSZipFilesSource extends Source instanceof DynamicPropertyAccess::EnumeratedPropName {
JSZipFilesSource() { JSZipFilesSource() {
super.getSourceObject() = super.getSourceObject() =
API::moduleImport("jszip").getInstance().getMember("files").getAnImmediateUse() API::moduleImport("jszip").getInstance().getMember("files").asSource()
} }
} }
@@ -116,7 +116,7 @@ module ZipSlip {
.getMember(["forEach", "filter"]) .getMember(["forEach", "filter"])
.getParameter(0) .getParameter(0)
.getParameter(0) .getParameter(0)
.getAnImmediateUse() .asSource()
} }
} }

View File

@@ -27,6 +27,8 @@ predicate hasUnknownPropertyRead(LocalObject obj) {
or or
exists(obj.getAPropertyRead("hasOwnProperty")) exists(obj.getAPropertyRead("hasOwnProperty"))
or or
obj.flowsTo(DataFlow::globalVarRef("Object").getAMemberCall("hasOwn").getArgument(0))
or
exists(obj.getAPropertyRead("propertyIsEnumerable")) exists(obj.getAPropertyRead("propertyIsEnumerable"))
} }

View File

@@ -71,7 +71,7 @@
</p> </p>
<sample language="javascript"> <sample language="javascript">
^0\.\d+E?\d+$ // BAD /^0\.\d+E?\d+$/.test(str) // BAD
</sample> </sample>
<p> <p>

View File

@@ -9,6 +9,7 @@
* @tags correctness * @tags correctness
* security * security
* external/cwe/cwe-020 * external/cwe/cwe-020
* external/cwe/cwe-940
*/ */
import javascript import javascript

View File

@@ -45,7 +45,7 @@ where
or or
// the same thing, but with API-nodes if they happen to be available // the same thing, but with API-nodes if they happen to be available
exists(API::Node tlsInvk | tlsInvk.getAnInvocation() = tlsInvocation() | exists(API::Node tlsInvk | tlsInvk.getAnInvocation() = tlsInvocation() |
disable.getRhs() = tlsInvk.getAParameter().getMember("rejectUnauthorized").getARhs() disable.getRhs() = tlsInvk.getAParameter().getMember("rejectUnauthorized").asSink()
) )
) and ) and
disable.getRhs().(AnalyzedNode).getTheBooleanValue() = false disable.getRhs().(AnalyzedNode).getTheBooleanValue() = false

View File

@@ -143,7 +143,7 @@ API::CallNode passportAuthenticateCall() {
*/ */
API::CallNode nonSessionBasedAuthMiddleware() { API::CallNode nonSessionBasedAuthMiddleware() {
result = passportAuthenticateCall() and result = passportAuthenticateCall() and
result.getParameter(1).getMember("session").getARhs().mayHaveBooleanValue(false) result.getParameter(1).getMember("session").asSink().mayHaveBooleanValue(false)
} }
/** /**

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