Compare commits

..

4 Commits

Author SHA1 Message Date
Harry Maclean
9afdab917e Ruby: Only use library inputs for libraries
Use the application heuristics to control whether we consider public
method parameters to all be sources of remote flow.
2022-03-10 16:35:13 +13:00
Harry Maclean
1915fce2d1 Ruby: Add heuristic to guess app or library
This uses the presence of various gem-related files to guess whether the
codebase is a Ruby application or a Ruby gem.
2022-03-10 16:34:01 +13:00
Harry Maclean
7a5b72b8f1 Ruby: Library input sources for Command Injection
Consider parameters of any public method to be remote flow sources for
the command injection vulnerability. This has the potential to be
noisy, but it does find several new TPs in mechanize.
2022-03-10 16:16:31 +13:00
Harry Maclean
02794d95d4 Ruby: Model Kernel.open as a command execution
If the argument to Kernel.open begins with "|", the rest of the string
is executed as a shell command.
2022-03-10 16:15:14 +13:00
64 changed files with 1289 additions and 848 deletions

View File

@@ -6,7 +6,7 @@
* @kind path-problem
* @problem.severity warning
* @security-severity 6.5
* @precision high
* @precision medium
* @id cpp/system-data-exposure
* @tags security
* external/cwe/cwe-497

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The `cpp/system-data-exposure` query has been increased from `medium` to `high` precision, following a number of improvements to the query logic.

View File

@@ -51,7 +51,7 @@ namespace Semmle.Autobuild.CSharp.Tests
int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary<string, string>? env, out IList<string> stdOut)
{
var pattern = string.IsNullOrEmpty(args) ? cmd : cmd + " " + args;
var pattern = cmd + " " + args;
RunProcessIn.Add(pattern);
if (!RunProcessOut.TryGetValue(pattern, out var str))
@@ -62,7 +62,7 @@ namespace Semmle.Autobuild.CSharp.Tests
RunProcessWorkingDirectory.TryGetValue(pattern, out var wd);
if (wd != workingDirectory)
throw new ArgumentException($"Unexpected RunProcessWorkingDirectory, got {wd ?? "null"} expected {workingDirectory ?? "null"} in {pattern}");
throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern);
if (!RunProcess.TryGetValue(pattern, out var ret))
throw new ArgumentException("Missing RunProcess " + pattern);
@@ -72,12 +72,12 @@ namespace Semmle.Autobuild.CSharp.Tests
int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary<string, string>? env)
{
var pattern = string.IsNullOrEmpty(args) ? cmd : cmd + " " + args;
var pattern = cmd + " " + args;
RunProcessIn.Add(pattern);
RunProcessWorkingDirectory.TryGetValue(pattern, out var wd);
if (wd != workingDirectory)
throw new ArgumentException($"Unexpected RunProcessWorkingDirectory, got {wd ?? "null"} expected {workingDirectory ?? "null"} in {pattern}");
throw new ArgumentException("Missing RunProcessWorkingDirectory " + pattern);
if (!RunProcess.TryGetValue(pattern, out var ret))
throw new ArgumentException("Missing RunProcess " + pattern);
@@ -255,7 +255,7 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestAnd1()
{
var cmd = BuildScript.Create("abc", "def ghi", false, null, null) & BuildScript.Create("codeql", null, false, null, null);
var cmd = BuildScript.Create("abc", "def ghi", false, null, null) & BuildScript.Create("odasa", null, false, null, null);
actions.RunProcess["abc def ghi"] = 1;
cmd.Run(actions, StartCallback, EndCallback);
@@ -269,14 +269,14 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestAnd2()
{
var cmd = BuildScript.Create("codeql", null, false, null, null) & BuildScript.Create("abc", "def ghi", false, null, null);
var cmd = BuildScript.Create("odasa", null, false, null, null) & BuildScript.Create("abc", "def ghi", false, null, null);
actions.RunProcess["abc def ghi"] = 1;
actions.RunProcess["codeql"] = 0;
actions.RunProcess["odasa "] = 0;
cmd.Run(actions, StartCallback, EndCallback);
Assert.Equal("codeql", actions.RunProcessIn[0]);
Assert.Equal("codeql", startCallbackIn[0]);
Assert.Equal("odasa ", actions.RunProcessIn[0]);
Assert.Equal("odasa ", startCallbackIn[0]);
Assert.Equal("", endCallbackIn[0]);
Assert.Equal(0, endCallbackReturn[0]);
@@ -289,14 +289,14 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestOr1()
{
var cmd = BuildScript.Create("codeql", null, false, null, null) | BuildScript.Create("abc", "def ghi", false, null, null);
var cmd = BuildScript.Create("odasa", null, false, null, null) | BuildScript.Create("abc", "def ghi", false, null, null);
actions.RunProcess["abc def ghi"] = 1;
actions.RunProcess["codeql"] = 0;
actions.RunProcess["odasa "] = 0;
cmd.Run(actions, StartCallback, EndCallback);
Assert.Equal("codeql", actions.RunProcessIn[0]);
Assert.Equal("codeql", startCallbackIn[0]);
Assert.Equal("odasa ", actions.RunProcessIn[0]);
Assert.Equal("odasa ", startCallbackIn[0]);
Assert.Equal("", endCallbackIn[0]);
Assert.Equal(0, endCallbackReturn[0]);
Assert.Equal(1, endCallbackReturn.Count);
@@ -305,10 +305,10 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestOr2()
{
var cmd = BuildScript.Create("abc", "def ghi", false, null, null) | BuildScript.Create("codeql", null, false, null, null);
var cmd = BuildScript.Create("abc", "def ghi", false, null, null) | BuildScript.Create("odasa", null, false, null, null);
actions.RunProcess["abc def ghi"] = 1;
actions.RunProcess["codeql"] = 0;
actions.RunProcess["odasa "] = 0;
cmd.Run(actions, StartCallback, EndCallback);
Assert.Equal("abc def ghi", actions.RunProcessIn[0]);
@@ -316,8 +316,8 @@ namespace Semmle.Autobuild.CSharp.Tests
Assert.Equal("", endCallbackIn[0]);
Assert.Equal(1, endCallbackReturn[0]);
Assert.Equal("codeql", actions.RunProcessIn[1]);
Assert.Equal("codeql", startCallbackIn[1]);
Assert.Equal("odasa ", actions.RunProcessIn[1]);
Assert.Equal("odasa ", startCallbackIn[1]);
Assert.Equal("", endCallbackIn[1]);
Assert.Equal(0, endCallbackReturn[1]);
}
@@ -385,6 +385,9 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.GetEnvironmentVariable[$"CODEQL_EXTRACTOR_{codeqlUpperLanguage}_ROOT"] = $@"C:\codeql\{codeqlUpperLanguage.ToLowerInvariant()}";
actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java";
actions.GetEnvironmentVariable["CODEQL_PLATFORM"] = isWindows ? "win64" : "linux64";
actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa";
actions.GetEnvironmentVariable["SEMMLE_JAVA_HOME"] = @"C:\odasa\tools\java";
actions.GetEnvironmentVariable["SEMMLE_PLATFORM_TOOLS"] = @"C:\odasa\tools";
actions.GetEnvironmentVariable["LGTM_INDEX_VSTOOLS_VERSION"] = vsToolsVersion;
actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_ARGUMENTS"] = msBuildArguments;
actions.GetEnvironmentVariable["LGTM_INDEX_MSBUILD_PLATFORM"] = msBuildPlatform;
@@ -413,7 +416,7 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.RunProcess["cmd.exe /C dotnet --info"] = 0;
actions.RunProcess[@"cmd.exe /C dotnet clean C:\Project\test.csproj"] = 0;
actions.RunProcess[@"cmd.exe /C dotnet restore C:\Project\test.csproj"] = 0;
actions.RunProcess[@"cmd.exe /C dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0;
actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Project\test.csproj"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -440,7 +443,7 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.RunProcess["dotnet --info"] = 0;
actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0;
actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0;
actions.RunProcess[@"dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0;
actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Project/test.csproj"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -598,7 +601,7 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestLinuxBuildCommand()
{
actions.RunProcess["./build.sh --skip-tests"] = 0;
actions.RunProcess[@"C:\odasa/tools/odasa index --auto ""./build.sh --skip-tests"""] = 0;
actions.FileExists["csharp.log"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
@@ -619,8 +622,8 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
actions.RunProcess[@"/bin/chmod u+x C:\Project/build/build.sh"] = 0;
actions.RunProcess[@"C:\Project/build/build.sh"] = 0;
actions.RunProcessWorkingDirectory[@"C:\Project/build/build.sh"] = @"C:\Project/build";
actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = 0;
actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build/build.sh"] = @"C:\Project/build";
actions.FileExists["csharp.log"] = true;
var autobuilder = CreateAutoBuilder(false);
@@ -636,8 +639,8 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0;
actions.RunProcess[@"C:\Project/build.sh"] = 0;
actions.RunProcessWorkingDirectory[@"C:\Project/build.sh"] = @"C:\Project";
actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 0;
actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project";
actions.FileExists["csharp.log"] = false;
var autobuilder = CreateAutoBuilder(false);
@@ -653,8 +656,8 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
actions.RunProcess[@"/bin/chmod u+x C:\Project/build.sh"] = 0;
actions.RunProcess[@"C:\Project/build.sh"] = 5;
actions.RunProcessWorkingDirectory[@"C:\Project/build.sh"] = @"C:\Project";
actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = 5;
actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto C:\Project/build.sh"] = @"C:\Project";
actions.FileExists["csharp.log"] = true;
var autobuilder = CreateAutoBuilder(false);
@@ -668,8 +671,8 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.EnumerateDirectories[@"C:\Project"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
actions.RunProcess[@"cmd.exe /C C:\Project\build.bat"] = 0;
actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\Project\build.bat"] = @"C:\Project";
actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = 0;
actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = @"C:\Project";
actions.FileExists["csharp.log"] = true;
var autobuilder = CreateAutoBuilder(true);
@@ -683,10 +686,10 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.EnumerateDirectories[@"C:\Project"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
actions.RunProcess[@"cmd.exe /C C:\Project\build.bat"] = 1;
actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\Project\build.bat"] = @"C:\Project";
actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = 1;
actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\build.bat"] = @"C:\Project";
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\codeql index --xml --extensions config"] = 0;
actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config"] = 0;
actions.FileExists["csharp.log"] = true;
var autobuilder = CreateAutoBuilder(true, ignoreErrors: "true");
@@ -696,9 +699,9 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestWindowsCmdIgnoreErrors()
{
actions.RunProcess["cmd.exe /C ^\"build.cmd --skip-tests^\""] = 3;
actions.RunProcess["cmd.exe /C C:\\odasa\\tools\\odasa index --auto ^\"build.cmd --skip-tests^\""] = 3;
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0;
actions.RunProcess[@"cmd.exe /C C:\codeql\tools\codeql index --xml --extensions config"] = 0;
actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config"] = 0;
actions.FileExists["csharp.log"] = true;
SkipVsWhere();
@@ -715,9 +718,9 @@ namespace Semmle.Autobuild.CSharp.Tests
public void TestWindowCSharpMsBuild()
{
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test1.sln -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test2.sln -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
@@ -746,9 +749,9 @@ namespace Semmle.Autobuild.CSharp.Tests
public void TestWindowCSharpMsBuildMultipleSolutions()
{
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test1.csproj -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test2.csproj -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Project\test1.csproj"] = true;
actions.FileExists[@"C:\Project\test2.csproj"] = true;
@@ -791,7 +794,7 @@ namespace Semmle.Autobuild.CSharp.Tests
public void TestWindowCSharpMsBuildFailed()
{
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test1.sln -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 1;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 1;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
@@ -817,8 +820,8 @@ namespace Semmle.Autobuild.CSharp.Tests
[Fact]
public void TestSkipNugetMsBuild()
{
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false;
actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat"] = false;
@@ -862,7 +865,7 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.RunProcess["dotnet --info"] = 0;
actions.RunProcess[@"dotnet clean C:\Project/test.csproj"] = 0;
actions.RunProcess[@"dotnet restore C:\Project/test.csproj"] = 0;
actions.RunProcess[@"dotnet build --no-incremental /p:UseSharedCompilation=false --no-restore C:\Project/test.csproj"] = 0;
actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false --no-restore C:\Project/test.csproj"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Project/test.csproj"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -894,7 +897,7 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0;
actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0;
actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0;
actions.RunProcess[@"C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0;
actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists["test.csproj"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -929,7 +932,7 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.RunProcess[@"C:\Project/.dotnet/dotnet --info"] = 0;
actions.RunProcess[@"C:\Project/.dotnet/dotnet clean C:\Project/test.csproj"] = 0;
actions.RunProcess[@"C:\Project/.dotnet/dotnet restore C:\Project/test.csproj"] = 0;
actions.RunProcess[@"C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0;
actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project/test.csproj"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists["test.csproj"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -960,7 +963,7 @@ namespace Semmle.Autobuild.CSharp.Tests
actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet --info"] = 0;
actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet clean C:\Project\test.csproj"] = 0;
actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet restore C:\Project\test.csproj"] = 0;
actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0;
actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental /p:UseSharedCompilation=false C:\Project\test.csproj"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Project\test.csproj"] = true;
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
@@ -1008,7 +1011,7 @@ namespace Semmle.Autobuild.CSharp.Tests
{
actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\dirs.proj -DisableParallelProcessing"] = 1;
actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\dirs.proj -DisableParallelProcessing"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && msbuild C:\\Project\\dirs.proj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && set Platform=&& type NUL && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\dirs.proj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Project\a\test.csproj"] = true;
actions.FileExists[@"C:\Project\dirs.proj"] = true;
@@ -1052,7 +1055,7 @@ namespace Semmle.Autobuild.CSharp.Tests
{
actions.RunProcess[@"nuget restore C:\Project/dirs.proj -DisableParallelProcessing"] = 1;
actions.RunProcess[@"mono C:\Project/.nuget/nuget.exe restore C:\Project/dirs.proj -DisableParallelProcessing"] = 0;
actions.RunProcess[@"msbuild C:\Project/dirs.proj /p:UseSharedCompilation=false /t:rebuild /p:MvcBuildViews=true"] = 0;
actions.RunProcess[@"C:\odasa/tools/odasa index --auto msbuild C:\Project/dirs.proj /p:UseSharedCompilation=false /t:rebuild /p:MvcBuildViews=true"] = 0;
actions.FileExists["csharp.log"] = true;
actions.FileExists[@"C:\Project/a/test.csproj"] = true;
actions.FileExists[@"C:\Project/dirs.proj"] = true;

View File

@@ -240,7 +240,7 @@ namespace Semmle.Autobuild.CSharp
private static BuildScript GetBuildScript(Autobuilder builder, string? dotNetPath, IDictionary<string, string>? environment, string projOrSln)
{
var build = new CommandBuilder(builder.Actions, null, environment);
var script = build.RunCommand(DotNetCommand(builder.Actions, dotNetPath)).
var script = builder.MaybeIndex(build, DotNetCommand(builder.Actions, dotNetPath)).
Argument("build").
Argument("--no-incremental");

View File

@@ -17,6 +17,10 @@ namespace Semmle.Autobuild.CSharp
{
standalone = builder.Actions.PathCombine(builder.CodeQLExtractorLangRoot, "tools", builder.CodeQlPlatform, "Semmle.Extraction.CSharp.Standalone");
}
else if (builder.SemmlePlatformTools is not null)
{
standalone = builder.Actions.PathCombine(builder.SemmlePlatformTools, "csharp", "Semmle.Extraction.CSharp.Standalone");
}
else
{
return BuildScript.Failure;

View File

@@ -190,15 +190,19 @@ namespace Semmle.Autobuild.Shared
});
CodeQLExtractorLangRoot = Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_ROOT");
SemmlePlatformTools = Actions.GetEnvironmentVariable("SEMMLE_PLATFORM_TOOLS");
CodeQlPlatform = Actions.GetEnvironmentVariable("CODEQL_PLATFORM");
TrapDir =
Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR") ??
throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR has not been set.");
Actions.GetEnvironmentVariable("TRAP_FOLDER") ??
throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_TRAP_DIR or TRAP_FOLDER has not been set.");
SourceArchiveDir =
Actions.GetEnvironmentVariable($"CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_SOURCE_ARCHIVE_DIR") ??
throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_SOURCE_ARCHIVE_DIR has not been set.");
Actions.GetEnvironmentVariable("SOURCE_ARCHIVE") ??
throw new InvalidEnvironmentException($"The environment variable CODEQL_EXTRACTOR_{this.Options.Language.UpperCaseName}_SOURCE_ARCHIVE_DIR or SOURCE_ARCHIVE has not been set.");
}
protected string TrapDir { get; }
@@ -260,9 +264,34 @@ namespace Semmle.Autobuild.Shared
/// </summary>
public string? CodeQLExtractorLangRoot { get; }
/// <summary>
/// Value of SEMMLE_PLATFORM_TOOLS environment variable.
/// </summary>
public string? SemmlePlatformTools { get; }
/// <summary>
/// Value of CODEQL_PLATFORM environment variable.
/// </summary>
public string? CodeQlPlatform { get; }
/// <summary>
/// The absolute path of the odasa executable.
/// null if we are running in CodeQL.
/// </summary>
public string? Odasa
{
get
{
var semmleDist = Actions.GetEnvironmentVariable("SEMMLE_DIST");
return semmleDist is null ? null : Actions.PathCombine(semmleDist, "tools", "odasa");
}
}
/// <summary>
/// Construct a command that executed the given <paramref name="cmd"/> wrapped in
/// an <code>odasa --index</code>, unless indexing has been disabled, in which case
/// <paramref name="cmd"/> is run directly.
/// </summary>
public CommandBuilder MaybeIndex(CommandBuilder builder, string cmd) => Odasa is null ? builder.RunCommand(cmd) : builder.IndexCommand(Odasa, cmd);
}
}

View File

@@ -58,7 +58,7 @@ namespace Semmle.Autobuild.Shared
if (vsTools is not null)
command.CallBatFile(vsTools.Path);
command.RunCommand(scriptPath);
builder.MaybeIndex(command, scriptPath);
return command.Script;
});
}

View File

@@ -26,7 +26,7 @@
var vsTools = MsBuildRule.GetVcVarsBatFile(builder);
if (vsTools is not null)
command.CallBatFile(vsTools.Path);
command.RunCommand(builder.Options.BuildCommand);
builder.MaybeIndex(command, builder.Options.BuildCommand);
return command.Script;
});

View File

@@ -70,7 +70,7 @@ namespace Semmle.Autobuild.Shared
this.environment = environment;
}
public override string ToString() => arguments.Length > 0 ? exe + " " + arguments : exe;
public override string ToString() => exe + " " + arguments;
public override int Run(IBuildActions actions, Action<string, bool> startCallback, Action<int, string, bool> exitCallBack)
{

View File

@@ -45,6 +45,11 @@ namespace Semmle.Autobuild.Shared
this.silent = silent;
}
private void OdasaIndex(string odasa)
{
RunCommand(odasa, "index --auto");
}
public CommandBuilder CallBatFile(string batFile, string? argumentsOpt = null)
{
NextCommand();
@@ -54,6 +59,21 @@ namespace Semmle.Autobuild.Shared
return this;
}
/// <summary>
/// Perform odasa index on a given command or BAT file.
/// </summary>
/// <param name="odasa">The odasa executable.</param>
/// <param name="command">The command to run.</param>
/// <param name="argumentsOpt">Additional arguments.</param>
/// <returns>this for chaining calls.</returns>
public CommandBuilder IndexCommand(string odasa, string command, string? argumentsOpt = null)
{
OdasaIndex(odasa);
QuoteArgument(command);
Argument(argumentsOpt);
return this;
}
private static readonly char[] specialChars = { ' ', '\t', '\n', '\v', '\"' };
private static readonly char[] cmdMetacharacter = { '(', ')', '%', '!', '^', '\"', '<', '>', '&', '|' };

View File

@@ -95,7 +95,7 @@ namespace Semmle.Autobuild.Shared
command.RunCommand("set Platform=&& type NUL", quoteExe: false);
}
command.RunCommand(msBuild);
builder.MaybeIndex(command, msBuild);
command.QuoteArgument(projectOrSolution.FullPath);
command.Argument("/p:UseSharedCompilation=false");

View File

@@ -0,0 +1,14 @@
**/mcs.exe:
**/csc.exe:
invoke ${env.SEMMLE_PLATFORM_TOOLS}/csharp/Semmle.Extraction.CSharp.Driver
prepend --compiler
prepend "${compiler}"
prepend --cil
**/mono*:
**/dotnet:
invoke ${odasa_tools}/extract-csharp.sh
**/msbuild:
**/xbuild:
replace yes
invoke ${compiler}
append /p:UseSharedCompilation=false

View File

@@ -0,0 +1,16 @@
#!/bin/bash
echo extract-csharp.sh: Called with arguments: "$@"
extractor=$SEMMLE_PLATFORM_TOOLS/csharp/Semmle.Extraction.CSharp.Driver
for i in "$@"
do
shift
if [[ `basename -- "$i"` =~ csc.exe|mcs.exe|csc.dll ]]
then
echo extract-csharp.sh: exec $extractor --cil $@
exec "$extractor" --compiler $i --cil $@
fi
done
echo extract-csharp.sh: Not a compiler invocation

View File

@@ -0,0 +1,9 @@
**\fakes*.exe:
**\moles*.exe:
order compiler
trace no
**\csc*.exe:
invoke ${env.SEMMLE_PLATFORM_TOOLS}\csharp\Semmle.Extraction.CSharp.Driver.exe
prepend --compiler
prepend "${compiler}"
prepend --cil

View File

@@ -20,11 +20,11 @@ namespace Semmle.Extraction.CIL.Driver
Console.WriteLine(" path A directory/dll/exe to analyze");
}
private static void ExtractAssembly(string assemblyPath, ILogger logger, CommonOptions options)
private static void ExtractAssembly(Layout layout, string assemblyPath, ILogger logger, CommonOptions options)
{
var sw = new Stopwatch();
sw.Start();
Analyser.ExtractCIL(assemblyPath, logger, options, out _, out _);
Analyser.ExtractCIL(layout, assemblyPath, logger, options, out _, out _);
sw.Stop();
logger.Log(Severity.Info, " {0} ({1})", assemblyPath, sw.Elapsed);
}
@@ -38,11 +38,12 @@ namespace Semmle.Extraction.CIL.Driver
}
var options = new ExtractorOptions(args);
var layout = new Layout();
using var logger = new ConsoleLogger(options.Verbosity);
var actions = options.AssembliesToExtract
.Select(asm => asm.Filename)
.Select<string, Action>(filename => () => ExtractAssembly(filename, logger, options))
.Select<string, Action>(filename => () => ExtractAssembly(layout, filename, logger, options))
.ToArray();
foreach (var missingRef in options.MissingReferences)

View File

@@ -25,7 +25,7 @@ namespace Semmle.Extraction.CIL
/// <param name="extractPdbs">Whether to extract PDBs.</param>
/// <param name="trapFile">The path of the trap file.</param>
/// <param name="extracted">Whether the file was extracted (false=cached).</param>
public static void ExtractCIL(string assemblyPath, ILogger logger, CommonOptions options, out string trapFile, out bool extracted)
public static void ExtractCIL(Layout layout, string assemblyPath, ILogger logger, CommonOptions options, out string trapFile, out bool extracted)
{
trapFile = "";
extracted = false;
@@ -35,7 +35,8 @@ namespace Semmle.Extraction.CIL
var pathTransformer = new PathTransformer(canonicalPathCache);
var extractor = new TracingExtractor(assemblyPath, logger, pathTransformer, options);
var transformedAssemblyPath = pathTransformer.Transform(assemblyPath);
using var trapWriter = transformedAssemblyPath.WithSuffix(".cil").CreateTrapWriter(logger, options.TrapCompression, discardDuplicates: true);
var project = layout.LookupProjectOrDefault(transformedAssemblyPath);
using var trapWriter = project.CreateTrapWriter(logger, transformedAssemblyPath.WithSuffix(".cil"), options.TrapCompression, discardDuplicates: true);
trapFile = trapWriter.TrapFile;
if (!options.Cache || !System.IO.File.Exists(trapFile))
{

View File

@@ -84,7 +84,7 @@ namespace Semmle.Extraction.CSharp.Standalone
foreach (var r in DesktopRuntimes)
yield return r;
// A bad choice if it's the self-contained runtime distributed in codeql dist.
// A bad choice if it's the self-contained runtime distributed in odasa dist.
yield return ExecutingRuntime;
}
}

View File

@@ -14,6 +14,7 @@ namespace Semmle.Extraction.CSharp
public void Initialize(CSharpCompilation compilationIn, CommonOptions options)
{
compilation = compilationIn;
layout = new Layout();
extractor = new StandaloneExtractor(Logger, PathTransformer, options);
this.options = options;
LogExtractorInfo(Extraction.Extractor.Version);

View File

@@ -18,6 +18,7 @@ namespace Semmle.Extraction.CSharp
{
protected Extraction.Extractor? extractor;
protected CSharpCompilation? compilation;
protected Layout? layout;
protected CommonOptions? options;
private readonly object progressMutex = new object();
@@ -124,7 +125,8 @@ namespace Semmle.Extraction.CSharp
var assemblyPath = r.FilePath!;
var transformedAssemblyPath = PathTransformer.Transform(assemblyPath);
using var trapWriter = transformedAssemblyPath.CreateTrapWriter(Logger, options.TrapCompression, discardDuplicates: true);
var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath);
using var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, options.TrapCompression, discardDuplicates: true);
var skipExtraction = options.Cache && File.Exists(trapWriter.TrapFile);
@@ -176,7 +178,7 @@ namespace Semmle.Extraction.CSharp
{
var stopwatch = new Stopwatch();
stopwatch.Start();
CIL.Analyser.ExtractCIL(r.FilePath!, Logger, options, out var trapFile, out var extracted);
CIL.Analyser.ExtractCIL(layout, r.FilePath!, Logger, options, out var trapFile, out var extracted);
stopwatch.Stop();
ReportProgress(r.FilePath, trapFile, stopwatch.Elapsed, extracted ? AnalysisAction.Extracted : AnalysisAction.UpToDate);
}
@@ -190,35 +192,44 @@ namespace Semmle.Extraction.CSharp
var sourcePath = tree.FilePath;
var transformedSourcePath = PathTransformer.Transform(sourcePath);
var trapPath = transformedSourcePath.GetTrapPath(Logger, options.TrapCompression);
var projectLayout = layout.LookupProjectOrNull(transformedSourcePath);
var excluded = projectLayout is null;
var trapPath = excluded ? "" : projectLayout!.GetTrapPath(Logger, transformedSourcePath, options.TrapCompression);
var upToDate = false;
// compilation.Clone() is used to allow symbols to be garbage collected.
using var trapWriter = transformedSourcePath.CreateTrapWriter(Logger, options.TrapCompression, discardDuplicates: false);
upToDate = options.Fast && FileIsUpToDate(sourcePath, trapWriter.TrapFile);
if (!upToDate)
if (!excluded)
{
var cx = new Context(extractor, compilation.Clone(), trapWriter, new SourceScope(tree), addAssemblyTrapPrefix);
// Ensure that the file itself is populated in case the source file is totally empty
var root = tree.GetRoot();
Entities.File.Create(cx, root.SyntaxTree.FilePath);
// compilation.Clone() is used to allow symbols to be garbage collected.
using var trapWriter = projectLayout!.CreateTrapWriter(Logger, transformedSourcePath, options.TrapCompression, discardDuplicates: false);
var csNode = (CSharpSyntaxNode)root;
var directiveVisitor = new DirectiveVisitor(cx);
csNode.Accept(directiveVisitor);
foreach (var branch in directiveVisitor.BranchesTaken)
upToDate = options.Fast && FileIsUpToDate(sourcePath, trapWriter.TrapFile);
if (!upToDate)
{
cx.TrapStackSuffix.Add(branch);
var cx = new Context(extractor, compilation.Clone(), trapWriter, new SourceScope(tree), addAssemblyTrapPrefix);
// Ensure that the file itself is populated in case the source file is totally empty
var root = tree.GetRoot();
Entities.File.Create(cx, root.SyntaxTree.FilePath);
var csNode = (CSharpSyntaxNode)root;
var directiveVisitor = new DirectiveVisitor(cx);
csNode.Accept(directiveVisitor);
foreach (var branch in directiveVisitor.BranchesTaken)
{
cx.TrapStackSuffix.Add(branch);
}
csNode.Accept(new CompilationUnitVisitor(cx));
cx.PopulateAll();
CommentPopulator.ExtractCommentBlocks(cx, cx.CommentGenerator);
cx.PopulateAll();
}
csNode.Accept(new CompilationUnitVisitor(cx));
cx.PopulateAll();
CommentPopulator.ExtractCommentBlocks(cx, cx.CommentGenerator);
cx.PopulateAll();
}
ReportProgress(sourcePath, trapPath, stopwatch.Elapsed, upToDate ? AnalysisAction.UpToDate : AnalysisAction.Extracted);
ReportProgress(sourcePath, trapPath, stopwatch.Elapsed, excluded
? AnalysisAction.Excluded
: upToDate
? AnalysisAction.UpToDate
: AnalysisAction.Extracted);
}
catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
{

View File

@@ -452,6 +452,19 @@ namespace Semmle.Extraction.CSharp
if (!string.IsNullOrEmpty(codeQlLogDir))
return codeQlLogDir;
var snapshot = Environment.GetEnvironmentVariable("ODASA_SNAPSHOT");
if (!string.IsNullOrEmpty(snapshot))
return Path.Combine(snapshot, "log");
var buildErrorDir = Environment.GetEnvironmentVariable("ODASA_BUILD_ERROR_DIR");
if (!string.IsNullOrEmpty(buildErrorDir))
// Used by `qltest`
return buildErrorDir;
var traps = Environment.GetEnvironmentVariable("TRAP_FOLDER");
if (!string.IsNullOrEmpty(traps))
return traps;
return Directory.GetCurrentDirectory();
}
}

View File

@@ -40,7 +40,8 @@ namespace Semmle.Extraction.CSharp
public static Options CreateWithEnvironment(string[] arguments)
{
var options = new Options();
var extractionOptions = Environment.GetEnvironmentVariable("LGTM_INDEX_EXTRACTOR");
var extractionOptions = Environment.GetEnvironmentVariable("SEMMLE_EXTRACTOR_OPTIONS") ??
Environment.GetEnvironmentVariable("LGTM_INDEX_EXTRACTOR");
var argsList = new List<string>(arguments);

View File

@@ -46,6 +46,7 @@ namespace Semmle.Extraction.CSharp
{
if (!init)
throw new InternalError("EndInitialize called without BeginInitialize returning true");
this.layout = new Layout();
this.options = options;
this.compilation = compilation;
this.extractor = new TracingExtractor(GetOutputName(compilation, commandLineArguments), Logger, PathTransformer, options);
@@ -201,7 +202,8 @@ namespace Semmle.Extraction.CSharp
var assemblyPath = ((TracingExtractor?)extractor).OutputPath;
var transformedAssemblyPath = PathTransformer.Transform(assemblyPath);
var assembly = compilation.Assembly;
var trapWriter = transformedAssemblyPath.CreateTrapWriter(Logger, options.TrapCompression, discardDuplicates: false);
var projectLayout = layout.LookupProjectOrDefault(transformedAssemblyPath);
var trapWriter = projectLayout.CreateTrapWriter(Logger, transformedAssemblyPath, options.TrapCompression, discardDuplicates: false);
compilationTrapFile = trapWriter; // Dispose later
var cx = new Context(extractor, compilation.Clone(), trapWriter, new AssemblyScope(assembly, assemblyPath), addAssemblyTrapPrefix);

View File

@@ -0,0 +1,231 @@
using System.IO;
using Xunit;
using Semmle.Util.Logging;
using System.Runtime.InteropServices;
namespace Semmle.Extraction.Tests
{
internal struct TransformedPathStub : PathTransformer.ITransformedPath
{
private readonly string value;
public TransformedPathStub(string value) => this.value = value;
public string Value => value;
public string Extension => throw new System.NotImplementedException();
public string NameWithoutExtension => throw new System.NotImplementedException();
public PathTransformer.ITransformedPath ParentDirectory => throw new System.NotImplementedException();
public string DatabaseId => throw new System.NotImplementedException();
public PathTransformer.ITransformedPath WithSuffix(string suffix)
{
throw new System.NotImplementedException();
}
}
public class Layout
{
private readonly ILogger logger = new LoggerMock();
[Fact]
public void TestDefaultLayout()
{
var layout = new Semmle.Extraction.Layout(null, null, null);
var project = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs"));
Assert.NotNull(project);
// All files are mapped when there's no layout file.
Assert.True(layout.FileInLayout(new TransformedPathStub("foo.cs")));
// Test trap filename
var tmpDir = Path.GetTempPath();
Directory.SetCurrentDirectory(tmpDir);
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
// `Directory.SetCurrentDirectory()` seems to slightly change the path on macOS,
// so adjusting it:
Assert.NotEqual(Directory.GetCurrentDirectory(), tmpDir);
tmpDir = "/private" + tmpDir;
// Remove trailing slash:
Assert.Equal('/', tmpDir[tmpDir.Length - 1]);
tmpDir = tmpDir.Substring(0, tmpDir.Length - 1);
Assert.Equal(Directory.GetCurrentDirectory(), tmpDir);
}
var f1 = project!.GetTrapPath(logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip);
var g1 = TrapWriter.NestPaths(logger, tmpDir, "foo.cs.trap.gz");
Assert.Equal(f1, g1);
// Test trap file generation
var trapwriterFilename = project.GetTrapPath(logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip);
using (var trapwriter = project.CreateTrapWriter(logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip, discardDuplicates: false))
{
trapwriter.Emit("1=*");
Assert.False(File.Exists(trapwriterFilename));
}
Assert.True(File.Exists(trapwriterFilename));
File.Delete(trapwriterFilename);
}
[Fact]
public void TestLayoutFile()
{
File.WriteAllLines("layout.txt", new string[]
{
"# Section",
"TRAP_FOLDER=" + Path.GetFullPath("snapshot\\trap"),
"ODASA_DB=snapshot\\db-csharp",
"SOURCE_ARCHIVE=" + Path.GetFullPath("snapshot\\archive"),
"ODASA_BUILD_ERROR_DIR=snapshot\build-errors",
"-foo.cs",
"bar.cs",
"-excluded",
"excluded/foo.cs",
"included"
});
var layout = new Semmle.Extraction.Layout(null, null, "layout.txt");
// Test general pattern matching
Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs")));
Assert.False(layout.FileInLayout(new TransformedPathStub("foo.cs")));
Assert.False(layout.FileInLayout(new TransformedPathStub("goo.cs")));
Assert.False(layout.FileInLayout(new TransformedPathStub("excluded/bar.cs")));
Assert.True(layout.FileInLayout(new TransformedPathStub("excluded/foo.cs")));
Assert.True(layout.FileInLayout(new TransformedPathStub("included/foo.cs")));
// Test the trap file
var project = layout.LookupProjectOrNull(new TransformedPathStub("bar.cs"));
Assert.NotNull(project);
var trapwriterFilename = project!.GetTrapPath(logger, new TransformedPathStub("bar.cs"), TrapWriter.CompressionMode.Gzip);
Assert.Equal(TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\trap"), "bar.cs.trap.gz"),
trapwriterFilename);
// Test the source archive
var trapWriter = project.CreateTrapWriter(logger, new TransformedPathStub("bar.cs"), TrapWriter.CompressionMode.Gzip, discardDuplicates: false);
trapWriter.Archive("layout.txt", new TransformedPathStub("layout.txt"), System.Text.Encoding.ASCII);
var writtenFile = TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\archive"), "layout.txt");
Assert.True(File.Exists(writtenFile));
File.Delete("layout.txt");
}
[Fact]
public void TestTrapOverridesLayout()
{
// When you specify both a trap file and a layout, use the trap file.
var layout = new Semmle.Extraction.Layout(Path.GetFullPath("snapshot\\trap"), null, "something.txt");
Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs")));
var subProject = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs"));
Assert.NotNull(subProject);
var f1 = subProject!.GetTrapPath(logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip);
var g1 = TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\trap"), "foo.cs.trap.gz");
Assert.Equal(f1, g1);
}
[Fact]
public void TestMultipleSections()
{
File.WriteAllLines("layout.txt", new string[]
{
"# Section 1",
"TRAP_FOLDER=" + Path.GetFullPath("snapshot\\trap1"),
"ODASA_DB=snapshot\\db-csharp",
"SOURCE_ARCHIVE=" + Path.GetFullPath("snapshot\\archive1"),
"ODASA_BUILD_ERROR_DIR=snapshot\build-errors",
"foo.cs",
"# Section 2",
"TRAP_FOLDER=" + Path.GetFullPath("snapshot\\trap2"),
"ODASA_DB=snapshot\\db-csharp",
"SOURCE_ARCHIVE=" + Path.GetFullPath("snapshot\\archive2"),
"ODASA_BUILD_ERROR_DIR=snapshot\build-errors",
"bar.cs",
});
var layout = new Semmle.Extraction.Layout(null, null, "layout.txt");
// Use Section 2
Assert.True(layout.FileInLayout(new TransformedPathStub("bar.cs")));
var subProject = layout.LookupProjectOrNull(new TransformedPathStub("bar.cs"));
Assert.NotNull(subProject);
var f1 = subProject!.GetTrapPath(logger, new TransformedPathStub("bar.cs"), TrapWriter.CompressionMode.Gzip);
var g1 = TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\trap2"), "bar.cs.trap.gz");
Assert.Equal(f1, g1);
// Use Section 1
Assert.True(layout.FileInLayout(new TransformedPathStub("foo.cs")));
subProject = layout.LookupProjectOrNull(new TransformedPathStub("foo.cs"));
Assert.NotNull(subProject);
var f2 = subProject!.GetTrapPath(logger, new TransformedPathStub("foo.cs"), TrapWriter.CompressionMode.Gzip);
var g2 = TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\trap1"), "foo.cs.trap.gz");
Assert.Equal(f2, g2);
// boo.dll is not in the layout, so use layout from first section.
Assert.False(layout.FileInLayout(new TransformedPathStub("boo.dll")));
var f3 = layout.LookupProjectOrDefault(new TransformedPathStub("boo.dll")).GetTrapPath(logger, new TransformedPathStub("boo.dll"), TrapWriter.CompressionMode.Gzip);
var g3 = TrapWriter.NestPaths(logger, Path.GetFullPath("snapshot\\trap1"), "boo.dll.trap.gz");
Assert.Equal(f3, g3);
// boo.cs is not in the layout, so return null
Assert.False(layout.FileInLayout(new TransformedPathStub("boo.cs")));
Assert.Null(layout.LookupProjectOrNull(new TransformedPathStub("boo.cs")));
}
[Fact]
public void MissingLayout()
{
Assert.Throws<Extraction.Layout.InvalidLayoutException>(() =>
new Semmle.Extraction.Layout(null, null, "nosuchfile.txt"));
}
[Fact]
public void EmptyLayout()
{
File.Create("layout.txt").Close();
Assert.Throws<Extraction.Layout.InvalidLayoutException>(() =>
new Semmle.Extraction.Layout(null, null, "layout.txt"));
}
[Fact]
public void InvalidLayout()
{
File.WriteAllLines("layout.txt", new string[]
{
"# Section 1"
});
Assert.Throws<Extraction.Layout.InvalidLayoutException>(() =>
new Semmle.Extraction.Layout(null, null, "layout.txt"));
}
private sealed class LoggerMock : ILogger
{
public void Dispose() { }
public void Log(Severity s, string text) { }
}
}
internal static class TrapWriterTestExtensions
{
public static void Emit(this TrapWriter trapFile, string s)
{
trapFile.Emit(new StringTrapEmitter(s));
}
private class StringTrapEmitter : ITrapEmitter
{
private readonly string content;
public StringTrapEmitter(string content)
{
this.content = content;
}
public void EmitTrap(TextWriter trapFile)
{
trapFile.Write(content);
}
}
}
}

View File

@@ -14,6 +14,7 @@ namespace Semmle.Extraction.Tests
public OptionsTests()
{
Environment.SetEnvironmentVariable("SEMMLE_EXTRACTOR_OPTIONS", "");
Environment.SetEnvironmentVariable("LGTM_INDEX_EXTRACTOR", "");
}
@@ -125,14 +126,14 @@ namespace Semmle.Extraction.Tests
[Fact]
public void EnvironmentVariables()
{
Environment.SetEnvironmentVariable("LGTM_INDEX_EXTRACTOR", "--cil c");
Environment.SetEnvironmentVariable("SEMMLE_EXTRACTOR_OPTIONS", "--cil c");
options = CSharp.Options.CreateWithEnvironment(new string[] { "a", "b" });
Assert.True(options.CIL);
Assert.Equal("a", options.CompilerArguments[0]);
Assert.Equal("b", options.CompilerArguments[1]);
Assert.Equal("c", options.CompilerArguments[2]);
Environment.SetEnvironmentVariable("LGTM_INDEX_EXTRACTOR", "");
Environment.SetEnvironmentVariable("SEMMLE_EXTRACTOR_OPTIONS", "");
Environment.SetEnvironmentVariable("LGTM_INDEX_EXTRACTOR", "--nocil");
options = CSharp.Options.CreateWithEnvironment(new string[] { "--cil" });
Assert.False(options.CIL);

View File

@@ -0,0 +1,204 @@
using Semmle.Util.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Semmle.Extraction
{
/// <summary>
/// An extractor layout file.
/// Represents the layout of projects into trap folders and source archives.
/// </summary>
public sealed class Layout
{
/// <summary>
/// Exception thrown when the layout file is invalid.
/// </summary>
public class InvalidLayoutException : Exception
{
public InvalidLayoutException(string file, string message) :
base("ODASA_CSHARP_LAYOUT " + file + " " + message)
{
}
}
/// <summary>
/// List of blocks in the layout file.
/// </summary>
private readonly List<LayoutBlock> blocks;
/// <summary>
/// A subproject in the layout file.
/// </summary>
public class SubProject
{
/// <summary>
/// The trap folder, or null for current directory.
/// </summary>
public string? TRAP_FOLDER { get; }
/// <summary>
/// The source archive, or null to skip.
/// </summary>
public string? SOURCE_ARCHIVE { get; }
public SubProject(string? traps, string? archive)
{
TRAP_FOLDER = traps;
SOURCE_ARCHIVE = archive;
}
/// <summary>
/// Gets the name of the trap file for a given source/assembly file.
/// </summary>
/// <param name="srcFile">The source file.</param>
/// <returns>The full filepath of the trap file.</returns>
public string GetTrapPath(ILogger logger, PathTransformer.ITransformedPath srcFile, TrapWriter.CompressionMode trapCompression) =>
TrapWriter.TrapPath(logger, TRAP_FOLDER, srcFile, trapCompression);
/// <summary>
/// Creates a trap writer for a given source/assembly file.
/// </summary>
/// <param name="srcFile">The source file.</param>
/// <returns>A newly created TrapWriter.</returns>
public TrapWriter CreateTrapWriter(ILogger logger, PathTransformer.ITransformedPath srcFile, TrapWriter.CompressionMode trapCompression, bool discardDuplicates) =>
new TrapWriter(logger, srcFile, TRAP_FOLDER, SOURCE_ARCHIVE, trapCompression, discardDuplicates);
}
private readonly SubProject defaultProject;
/// <summary>
/// Finds the suitable directories for a given source file.
/// Returns null if not included in the layout.
/// </summary>
/// <param name="sourceFile">The file to look up.</param>
/// <returns>The relevant subproject, or null if not found.</returns>
public SubProject? LookupProjectOrNull(PathTransformer.ITransformedPath sourceFile)
{
if (!useLayoutFile)
return defaultProject;
return blocks
.Where(block => block.Matches(sourceFile))
.Select(block => block.Directories)
.FirstOrDefault();
}
/// <summary>
/// Finds the suitable directories for a given source file.
/// Returns the default project if not included in the layout.
/// </summary>
/// <param name="sourceFile">The file to look up.</param>
/// <returns>The relevant subproject, or DefaultProject if not found.</returns>
public SubProject LookupProjectOrDefault(PathTransformer.ITransformedPath sourceFile)
{
return LookupProjectOrNull(sourceFile) ?? defaultProject;
}
private readonly bool useLayoutFile;
/// <summary>
/// Default constructor reads parameters from the environment.
/// </summary>
public Layout() : this(
Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_TRAP_DIR") ?? Environment.GetEnvironmentVariable("TRAP_FOLDER"),
Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR") ?? Environment.GetEnvironmentVariable("SOURCE_ARCHIVE"),
Environment.GetEnvironmentVariable("ODASA_CSHARP_LAYOUT"))
{
}
/// <summary>
/// Creates the project layout. Reads the layout file if specified.
/// </summary>
/// <param name="traps">Directory for trap files, or null to use layout/current directory.</param>
/// <param name="archive">Directory for source archive, or null for layout/no archive.</param>
/// <param name="layout">Path of layout file, or null for no layout.</param>
/// <exception cref="InvalidLayoutException">Failed to read layout file.</exception>
public Layout(string? traps, string? archive, string? layout)
{
useLayoutFile = string.IsNullOrEmpty(traps) && !string.IsNullOrEmpty(layout);
blocks = new List<LayoutBlock>();
if (useLayoutFile)
{
ReadLayoutFile(layout!);
defaultProject = blocks[0].Directories;
}
else
{
defaultProject = new SubProject(traps, archive);
}
}
/// <summary>
/// Is the source file included in the layout?
/// </summary>
/// <param name="path">The absolute path of the file to query.</param>
/// <returns>True iff there is no layout file or the layout file specifies the file.</returns>
public bool FileInLayout(PathTransformer.ITransformedPath path) => LookupProjectOrNull(path) is not null;
private void ReadLayoutFile(string layout)
{
try
{
var lines = File.ReadAllLines(layout);
var i = 0;
while (!lines[i].StartsWith("#"))
i++;
while (i < lines.Length)
{
var block = new LayoutBlock(lines, ref i);
blocks.Add(block);
}
if (blocks.Count == 0)
throw new InvalidLayoutException(layout, "contains no blocks");
}
catch (IOException ex)
{
throw new InvalidLayoutException(layout, ex.Message);
}
catch (IndexOutOfRangeException)
{
throw new InvalidLayoutException(layout, "is invalid");
}
}
}
internal sealed class LayoutBlock
{
private readonly List<FilePattern> filePatterns = new List<FilePattern>();
public Layout.SubProject Directories { get; }
private static string? ReadVariable(string name, string line)
{
var prefix = name + "=";
if (!line.StartsWith(prefix))
return null;
return line.Substring(prefix.Length).Trim();
}
public LayoutBlock(string[] lines, ref int i)
{
// first line: #name
i++;
var trapFolder = ReadVariable("TRAP_FOLDER", lines[i++]);
// Don't care about ODASA_DB.
ReadVariable("ODASA_DB", lines[i++]);
var sourceArchive = ReadVariable("SOURCE_ARCHIVE", lines[i++]);
Directories = new Layout.SubProject(trapFolder, sourceArchive);
// Don't care about ODASA_BUILD_ERROR_DIR.
ReadVariable("ODASA_BUILD_ERROR_DIR", lines[i++]);
while (i < lines.Length && !lines[i].StartsWith("#"))
{
filePatterns.Add(new FilePattern(lines[i++]));
}
}
public bool Matches(PathTransformer.ITransformedPath path) => FilePattern.Matches(filePatterns, path.Value, out var _);
}
}

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Diagnostics.CodeAnalysis;
using Semmle.Util;
using Semmle.Util.Logging;
namespace Semmle.Extraction
{
@@ -36,20 +35,6 @@ namespace Semmle.Extraction
ITransformedPath WithSuffix(string suffix);
string DatabaseId { get; }
/// <summary>
/// Gets the name of the trap file for this file.
/// </summary>
/// <returns>The full filepath of the trap file.</returns>
public string GetTrapPath(ILogger logger, TrapWriter.CompressionMode trapCompression) =>
TrapWriter.TrapPath(logger, Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"), this, trapCompression);
/// <summary>
/// Creates a trap writer for this file.
/// </summary>
/// <returns>A newly created TrapWriter.</returns>
public TrapWriter CreateTrapWriter(ILogger logger, TrapWriter.CompressionMode trapCompression, bool discardDuplicates) =>
new(logger, this, Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"), Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"), trapCompression, discardDuplicates);
}
private struct TransformedPath : ITransformedPath

View File

@@ -234,7 +234,9 @@ namespace Semmle.Util
/// <returns>A new CanonicalPathCache.</returns>
public static CanonicalPathCache Create(ILogger logger, int maxCapacity)
{
var preserveSymlinks = Environment.GetEnvironmentVariable("CODEQL_PRESERVE_SYMLINKS") == "true";
var preserveSymlinks =
Environment.GetEnvironmentVariable("CODEQL_PRESERVE_SYMLINKS") == "true" ||
Environment.GetEnvironmentVariable("SEMMLE_PRESERVE_SYMLINKS") == "true";
return Create(logger, maxCapacity, preserveSymlinks ? CanonicalPathCache.Symlinks.Preserve : CanonicalPathCache.Symlinks.Follow);
}

View File

@@ -74,7 +74,7 @@ namespace Semmle.Util.Logging
}
catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
{
Console.Error.WriteLine("CodeQL: Couldn't initialise C# extractor output: " + ex.Message + "\n" + ex.StackTrace);
Console.Error.WriteLine("SEMMLE: Couldn't initialise C# extractor output: " + ex.Message + "\n" + ex.StackTrace);
Console.Error.Flush();
throw;
}

View File

@@ -1,15 +0,0 @@
---
category: breaking
---
* The C# extractor no longer supports the following legacy environment variables:
```
ODASA_BUILD_ERROR_DIR
ODASA_CSHARP_LAYOUT
ODASA_SNAPSHOT
SEMMLE_DIST
SEMMLE_EXTRACTOR_OPTIONS
SEMMLE_PLATFORM_TOOLS
SEMMLE_PRESERVE_SYMLINKS
SOURCE_ARCHIVE
TRAP_FOLDER
```

View File

@@ -36,18 +36,12 @@ module ArrayTaintTracking {
succ = call
)
or
// `array.filter(x => x)` and `array.filter(x => !!x)` keeps the taint
// `array.filter(x => x)` keeps the taint
call.(DataFlow::MethodCallNode).getMethodName() = "filter" and
pred = call.getReceiver() and
succ = call and
exists(DataFlow::FunctionNode callback, DataFlow::Node param, DataFlow::Node ret |
callback = call.getArgument(0).getAFunctionValue() and
param = callback.getParameter(0).getALocalUse() and
ret = callback.getAReturn()
|
param = ret
or
param = DataFlow::exprNode(ret.asExpr().(LogNotExpr).getOperand().(LogNotExpr).getOperand())
exists(DataFlow::FunctionNode callback | callback = call.getArgument(0).getAFunctionValue() |
callback.getParameter(0).getALocalUse() = callback.getAReturn()
)
or
// `array.reduce` with tainted value in callback

View File

@@ -1,25 +0,0 @@
| arrays.js:2:16:2:23 | "source" | arrays.js:5:8:5:14 | obj.foo |
| arrays.js:2:16:2:23 | "source" | arrays.js:11:10:11:15 | arr[i] |
| arrays.js:2:16:2:23 | "source" | arrays.js:15:27:15:27 | e |
| arrays.js:2:16:2:23 | "source" | arrays.js:16:23:16:23 | e |
| arrays.js:2:16:2:23 | "source" | arrays.js:20:8:20:16 | arr.pop() |
| arrays.js:2:16:2:23 | "source" | arrays.js:49:8:49:13 | arr[0] |
| arrays.js:2:16:2:23 | "source" | arrays.js:52:10:52:10 | x |
| arrays.js:2:16:2:23 | "source" | arrays.js:56:10:56:10 | x |
| arrays.js:2:16:2:23 | "source" | arrays.js:60:10:60:10 | x |
| arrays.js:2:16:2:23 | "source" | arrays.js:66:10:66:10 | x |
| arrays.js:2:16:2:23 | "source" | arrays.js:71:10:71:10 | x |
| arrays.js:2:16:2:23 | "source" | arrays.js:74:8:74:29 | arr.fin ... llback) |
| arrays.js:2:16:2:23 | "source" | arrays.js:77:8:77:35 | arrayFi ... llback) |
| arrays.js:2:16:2:23 | "source" | arrays.js:81:10:81:10 | x |
| arrays.js:18:22:18:29 | "source" | arrays.js:18:50:18:50 | e |
| arrays.js:22:15:22:22 | "source" | arrays.js:23:8:23:17 | arr2.pop() |
| arrays.js:25:15:25:22 | "source" | arrays.js:26:8:26:17 | arr3.pop() |
| arrays.js:29:21:29:28 | "source" | arrays.js:30:8:30:17 | arr4.pop() |
| arrays.js:29:21:29:28 | "source" | arrays.js:33:8:33:17 | arr5.pop() |
| arrays.js:29:21:29:28 | "source" | arrays.js:35:8:35:26 | arr5.slice(2).pop() |
| arrays.js:29:21:29:28 | "source" | arrays.js:41:8:41:17 | arr6.pop() |
| arrays.js:44:4:44:11 | "source" | arrays.js:45:10:45:18 | ary.pop() |
| arrays.js:44:4:44:11 | "source" | arrays.js:46:10:46:12 | ary |
| arrays.js:84:9:84:16 | "source" | arrays.js:84:8:84:34 | ["sourc ... ) => x) |
| arrays.js:85:9:85:16 | "source" | arrays.js:85:8:85:36 | ["sourc ... => !!x) |

View File

@@ -1,15 +0,0 @@
import javascript
class ArrayTaintFlowConfig extends TaintTracking::Configuration {
ArrayTaintFlowConfig() { this = "ArrayTaintFlowConfig" }
override predicate isSource(DataFlow::Node source) { source.asExpr().getStringValue() = "source" }
override predicate isSink(DataFlow::Node sink) {
sink = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument()
}
}
from ArrayTaintFlowConfig config, DataFlow::Node src, DataFlow::Node snk
where config.hasFlow(src, snk)
select src, snk

View File

@@ -80,7 +80,4 @@
for (const x of uniq(arr)) {
sink(x); // NOT OK
}
sink(["source"].filter((x) => x))
sink(["source"].filter((x) => !!x))
});

View File

@@ -1,9 +1,9 @@
nodes
| arrays.js:1:1:86:2 | [ParExpr] (functi ... !x)) }) | semmle.label | [ParExpr] (functi ... !x)) }) |
| arrays.js:1:1:86:3 | [ExprStmt] (functi ... x)) }); | semmle.label | [ExprStmt] (functi ... x)) }); |
| arrays.js:1:1:86:3 | [ExprStmt] (functi ... x)) }); | semmle.order | 1 |
| arrays.js:1:2:86:1 | [FunctionExpr] functio ... !!x)) } | semmle.label | [FunctionExpr] functio ... !!x)) } |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | semmle.label | [BlockStmt] { let ... !!x)) } |
| arrays.js:1:1:83:2 | [ParExpr] (functi ... } }) | semmle.label | [ParExpr] (functi ... } }) |
| arrays.js:1:1:83:3 | [ExprStmt] (functi ... } }); | semmle.label | [ExprStmt] (functi ... } }); |
| arrays.js:1:1:83:3 | [ExprStmt] (functi ... } }); | semmle.order | 1 |
| arrays.js:1:2:83:1 | [FunctionExpr] functio ... K } } | semmle.label | [FunctionExpr] functio ... K } } |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | semmle.label | [BlockStmt] { let ... K } } |
| arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | [DeclStmt] let source = ... |
| arrays.js:2:7:2:12 | [VarDecl] source | semmle.label | [VarDecl] source |
| arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.label | [VariableDeclarator] source = "source" |
@@ -339,30 +339,6 @@ nodes
| arrays.js:81:5:81:11 | [CallExpr] sink(x) | semmle.label | [CallExpr] sink(x) |
| arrays.js:81:5:81:12 | [ExprStmt] sink(x); | semmle.label | [ExprStmt] sink(x); |
| arrays.js:81:10:81:10 | [VarRef] x | semmle.label | [VarRef] x |
| arrays.js:84:3:84:6 | [VarRef] sink | semmle.label | [VarRef] sink |
| arrays.js:84:3:84:35 | [CallExpr] sink([" ... => x)) | semmle.label | [CallExpr] sink([" ... => x)) |
| arrays.js:84:3:84:35 | [ExprStmt] sink([" ... => x)) | semmle.label | [ExprStmt] sink([" ... => x)) |
| arrays.js:84:8:84:17 | [ArrayExpr] ["source"] | semmle.label | [ArrayExpr] ["source"] |
| arrays.js:84:8:84:24 | [DotExpr] ["source"].filter | semmle.label | [DotExpr] ["source"].filter |
| arrays.js:84:8:84:34 | [MethodCallExpr] ["sourc ... ) => x) | semmle.label | [MethodCallExpr] ["sourc ... ) => x) |
| arrays.js:84:9:84:16 | [Literal] "source" | semmle.label | [Literal] "source" |
| arrays.js:84:19:84:24 | [Label] filter | semmle.label | [Label] filter |
| arrays.js:84:26:84:33 | [ArrowFunctionExpr] (x) => x | semmle.label | [ArrowFunctionExpr] (x) => x |
| arrays.js:84:27:84:27 | [SimpleParameter] x | semmle.label | [SimpleParameter] x |
| arrays.js:84:33:84:33 | [VarRef] x | semmle.label | [VarRef] x |
| arrays.js:85:3:85:6 | [VarRef] sink | semmle.label | [VarRef] sink |
| arrays.js:85:3:85:37 | [CallExpr] sink([" ... > !!x)) | semmle.label | [CallExpr] sink([" ... > !!x)) |
| arrays.js:85:3:85:37 | [ExprStmt] sink([" ... > !!x)) | semmle.label | [ExprStmt] sink([" ... > !!x)) |
| arrays.js:85:8:85:17 | [ArrayExpr] ["source"] | semmle.label | [ArrayExpr] ["source"] |
| arrays.js:85:8:85:24 | [DotExpr] ["source"].filter | semmle.label | [DotExpr] ["source"].filter |
| arrays.js:85:8:85:36 | [MethodCallExpr] ["sourc ... => !!x) | semmle.label | [MethodCallExpr] ["sourc ... => !!x) |
| arrays.js:85:9:85:16 | [Literal] "source" | semmle.label | [Literal] "source" |
| arrays.js:85:19:85:24 | [Label] filter | semmle.label | [Label] filter |
| arrays.js:85:26:85:35 | [ArrowFunctionExpr] (x) => !!x | semmle.label | [ArrowFunctionExpr] (x) => !!x |
| arrays.js:85:27:85:27 | [SimpleParameter] x | semmle.label | [SimpleParameter] x |
| arrays.js:85:33:85:35 | [UnaryExpr] !!x | semmle.label | [UnaryExpr] !!x |
| arrays.js:85:34:85:35 | [UnaryExpr] !x | semmle.label | [UnaryExpr] !x |
| arrays.js:85:35:85:35 | [VarRef] x | semmle.label | [VarRef] x |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
@@ -404,104 +380,94 @@ nodes
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
edges
| arrays.js:1:1:86:2 | [ParExpr] (functi ... !x)) }) | arrays.js:1:2:86:1 | [FunctionExpr] functio ... !!x)) } | semmle.label | 1 |
| arrays.js:1:1:86:2 | [ParExpr] (functi ... !x)) }) | arrays.js:1:2:86:1 | [FunctionExpr] functio ... !!x)) } | semmle.order | 1 |
| arrays.js:1:1:86:3 | [ExprStmt] (functi ... x)) }); | arrays.js:1:1:86:2 | [ParExpr] (functi ... !x)) }) | semmle.label | 1 |
| arrays.js:1:1:86:3 | [ExprStmt] (functi ... x)) }); | arrays.js:1:1:86:2 | [ParExpr] (functi ... !x)) }) | semmle.order | 1 |
| arrays.js:1:2:86:1 | [FunctionExpr] functio ... !!x)) } | arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | semmle.label | 5 |
| arrays.js:1:2:86:1 | [FunctionExpr] functio ... !!x)) } | arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | semmle.order | 5 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | 1 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.order | 1 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.label | 2 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.order | 2 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.label | 3 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.order | 3 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.label | 4 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.order | 4 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.label | 5 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.order | 5 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.label | 6 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.order | 6 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.label | 7 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.order | 7 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.label | 8 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.order | 8 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.label | 9 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.order | 9 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.label | 10 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.order | 10 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.label | 11 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.order | 11 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.label | 12 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.order | 12 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.label | 13 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.order | 13 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.label | 14 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.order | 14 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.label | 15 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.order | 15 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.label | 16 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.order | 16 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.label | 17 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.order | 17 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.label | 18 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.order | 18 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.label | 19 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.order | 19 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.label | 20 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.order | 20 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.label | 21 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.order | 21 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.label | 22 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.order | 22 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.label | 23 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.order | 23 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.label | 24 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.order | 24 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.label | 25 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.order | 25 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.label | 26 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.order | 26 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.label | 27 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.order | 27 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.label | 28 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.order | 28 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.label | 29 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.order | 29 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.label | 30 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.order | 30 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.label | 31 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.order | 31 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.label | 32 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.order | 32 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.label | 33 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.order | 33 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.label | 34 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.order | 34 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.label | 35 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.order | 35 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.label | 36 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.order | 36 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.label | 37 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.order | 37 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.label | 38 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.order | 38 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:84:3:84:35 | [ExprStmt] sink([" ... => x)) | semmle.label | 39 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:84:3:84:35 | [ExprStmt] sink([" ... => x)) | semmle.order | 39 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:85:3:85:37 | [ExprStmt] sink([" ... > !!x)) | semmle.label | 40 |
| arrays.js:1:14:86:1 | [BlockStmt] { let ... !!x)) } | arrays.js:85:3:85:37 | [ExprStmt] sink([" ... > !!x)) | semmle.order | 40 |
| arrays.js:1:1:83:2 | [ParExpr] (functi ... } }) | arrays.js:1:2:83:1 | [FunctionExpr] functio ... K } } | semmle.label | 1 |
| arrays.js:1:1:83:2 | [ParExpr] (functi ... } }) | arrays.js:1:2:83:1 | [FunctionExpr] functio ... K } } | semmle.order | 1 |
| arrays.js:1:1:83:3 | [ExprStmt] (functi ... } }); | arrays.js:1:1:83:2 | [ParExpr] (functi ... } }) | semmle.label | 1 |
| arrays.js:1:1:83:3 | [ExprStmt] (functi ... } }); | arrays.js:1:1:83:2 | [ParExpr] (functi ... } }) | semmle.order | 1 |
| arrays.js:1:2:83:1 | [FunctionExpr] functio ... K } } | arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | semmle.label | 5 |
| arrays.js:1:2:83:1 | [FunctionExpr] functio ... K } } | arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | semmle.order | 5 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | 1 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.order | 1 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.label | 2 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.order | 2 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.label | 3 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.order | 3 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.label | 4 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.order | 4 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.label | 5 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.order | 5 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.label | 6 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.order | 6 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.label | 7 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.order | 7 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.label | 8 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.order | 8 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.label | 9 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.order | 9 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.label | 10 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.order | 10 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.label | 11 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.order | 11 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.label | 12 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.order | 12 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.label | 13 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.order | 13 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.label | 14 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.order | 14 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.label | 15 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.order | 15 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.label | 16 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.order | 16 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.label | 17 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.order | 17 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.label | 18 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.order | 18 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.label | 19 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.order | 19 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.label | 20 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.order | 20 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.label | 21 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.order | 21 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.label | 22 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.order | 22 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.label | 23 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.order | 23 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.label | 24 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.order | 24 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.label | 25 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.order | 25 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.label | 26 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.order | 26 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.label | 27 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.order | 27 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.label | 28 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.order | 28 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.label | 29 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.order | 29 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.label | 30 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.order | 30 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.label | 31 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.order | 31 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.label | 32 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.order | 32 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.label | 33 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.order | 33 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.label | 34 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.order | 34 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.label | 35 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.order | 35 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.label | 36 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.order | 36 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.label | 37 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.order | 37 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.label | 38 |
| arrays.js:1:14:83:1 | [BlockStmt] { let ... K } } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.order | 38 |
| arrays.js:2:3:2:24 | [DeclStmt] let source = ... | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.label | 1 |
| arrays.js:2:3:2:24 | [DeclStmt] let source = ... | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.order | 1 |
| arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | arrays.js:2:7:2:12 | [VarDecl] source | semmle.label | 1 |
@@ -1086,50 +1052,6 @@ edges
| arrays.js:81:5:81:11 | [CallExpr] sink(x) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
| arrays.js:81:5:81:12 | [ExprStmt] sink(x); | arrays.js:81:5:81:11 | [CallExpr] sink(x) | semmle.label | 1 |
| arrays.js:81:5:81:12 | [ExprStmt] sink(x); | arrays.js:81:5:81:11 | [CallExpr] sink(x) | semmle.order | 1 |
| arrays.js:84:3:84:35 | [CallExpr] sink([" ... => x)) | arrays.js:84:3:84:6 | [VarRef] sink | semmle.label | 0 |
| arrays.js:84:3:84:35 | [CallExpr] sink([" ... => x)) | arrays.js:84:3:84:6 | [VarRef] sink | semmle.order | 0 |
| arrays.js:84:3:84:35 | [CallExpr] sink([" ... => x)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
| arrays.js:84:3:84:35 | [CallExpr] sink([" ... => x)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
| arrays.js:84:3:84:35 | [ExprStmt] sink([" ... => x)) | arrays.js:84:3:84:35 | [CallExpr] sink([" ... => x)) | semmle.label | 1 |
| arrays.js:84:3:84:35 | [ExprStmt] sink([" ... => x)) | arrays.js:84:3:84:35 | [CallExpr] sink([" ... => x)) | semmle.order | 1 |
| arrays.js:84:8:84:17 | [ArrayExpr] ["source"] | arrays.js:84:9:84:16 | [Literal] "source" | semmle.label | 1 |
| arrays.js:84:8:84:17 | [ArrayExpr] ["source"] | arrays.js:84:9:84:16 | [Literal] "source" | semmle.order | 1 |
| arrays.js:84:8:84:24 | [DotExpr] ["source"].filter | arrays.js:84:8:84:17 | [ArrayExpr] ["source"] | semmle.label | 1 |
| arrays.js:84:8:84:24 | [DotExpr] ["source"].filter | arrays.js:84:8:84:17 | [ArrayExpr] ["source"] | semmle.order | 1 |
| arrays.js:84:8:84:24 | [DotExpr] ["source"].filter | arrays.js:84:19:84:24 | [Label] filter | semmle.label | 2 |
| arrays.js:84:8:84:24 | [DotExpr] ["source"].filter | arrays.js:84:19:84:24 | [Label] filter | semmle.order | 2 |
| arrays.js:84:8:84:34 | [MethodCallExpr] ["sourc ... ) => x) | arrays.js:84:8:84:24 | [DotExpr] ["source"].filter | semmle.label | 0 |
| arrays.js:84:8:84:34 | [MethodCallExpr] ["sourc ... ) => x) | arrays.js:84:8:84:24 | [DotExpr] ["source"].filter | semmle.order | 0 |
| arrays.js:84:8:84:34 | [MethodCallExpr] ["sourc ... ) => x) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
| arrays.js:84:8:84:34 | [MethodCallExpr] ["sourc ... ) => x) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
| arrays.js:84:26:84:33 | [ArrowFunctionExpr] (x) => x | arrays.js:84:33:84:33 | [VarRef] x | semmle.label | 5 |
| arrays.js:84:26:84:33 | [ArrowFunctionExpr] (x) => x | arrays.js:84:33:84:33 | [VarRef] x | semmle.order | 5 |
| arrays.js:84:26:84:33 | [ArrowFunctionExpr] (x) => x | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
| arrays.js:84:26:84:33 | [ArrowFunctionExpr] (x) => x | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
| arrays.js:85:3:85:37 | [CallExpr] sink([" ... > !!x)) | arrays.js:85:3:85:6 | [VarRef] sink | semmle.label | 0 |
| arrays.js:85:3:85:37 | [CallExpr] sink([" ... > !!x)) | arrays.js:85:3:85:6 | [VarRef] sink | semmle.order | 0 |
| arrays.js:85:3:85:37 | [CallExpr] sink([" ... > !!x)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
| arrays.js:85:3:85:37 | [CallExpr] sink([" ... > !!x)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
| arrays.js:85:3:85:37 | [ExprStmt] sink([" ... > !!x)) | arrays.js:85:3:85:37 | [CallExpr] sink([" ... > !!x)) | semmle.label | 1 |
| arrays.js:85:3:85:37 | [ExprStmt] sink([" ... > !!x)) | arrays.js:85:3:85:37 | [CallExpr] sink([" ... > !!x)) | semmle.order | 1 |
| arrays.js:85:8:85:17 | [ArrayExpr] ["source"] | arrays.js:85:9:85:16 | [Literal] "source" | semmle.label | 1 |
| arrays.js:85:8:85:17 | [ArrayExpr] ["source"] | arrays.js:85:9:85:16 | [Literal] "source" | semmle.order | 1 |
| arrays.js:85:8:85:24 | [DotExpr] ["source"].filter | arrays.js:85:8:85:17 | [ArrayExpr] ["source"] | semmle.label | 1 |
| arrays.js:85:8:85:24 | [DotExpr] ["source"].filter | arrays.js:85:8:85:17 | [ArrayExpr] ["source"] | semmle.order | 1 |
| arrays.js:85:8:85:24 | [DotExpr] ["source"].filter | arrays.js:85:19:85:24 | [Label] filter | semmle.label | 2 |
| arrays.js:85:8:85:24 | [DotExpr] ["source"].filter | arrays.js:85:19:85:24 | [Label] filter | semmle.order | 2 |
| arrays.js:85:8:85:36 | [MethodCallExpr] ["sourc ... => !!x) | arrays.js:85:8:85:24 | [DotExpr] ["source"].filter | semmle.label | 0 |
| arrays.js:85:8:85:36 | [MethodCallExpr] ["sourc ... => !!x) | arrays.js:85:8:85:24 | [DotExpr] ["source"].filter | semmle.order | 0 |
| arrays.js:85:8:85:36 | [MethodCallExpr] ["sourc ... => !!x) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 |
| arrays.js:85:8:85:36 | [MethodCallExpr] ["sourc ... => !!x) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 |
| arrays.js:85:26:85:35 | [ArrowFunctionExpr] (x) => !!x | arrays.js:85:33:85:35 | [UnaryExpr] !!x | semmle.label | 5 |
| arrays.js:85:26:85:35 | [ArrowFunctionExpr] (x) => !!x | arrays.js:85:33:85:35 | [UnaryExpr] !!x | semmle.order | 5 |
| arrays.js:85:26:85:35 | [ArrowFunctionExpr] (x) => !!x | file://:0:0:0:0 | (Parameters) | semmle.label | 1 |
| arrays.js:85:26:85:35 | [ArrowFunctionExpr] (x) => !!x | file://:0:0:0:0 | (Parameters) | semmle.order | 1 |
| arrays.js:85:33:85:35 | [UnaryExpr] !!x | arrays.js:85:34:85:35 | [UnaryExpr] !x | semmle.label | 1 |
| arrays.js:85:33:85:35 | [UnaryExpr] !!x | arrays.js:85:34:85:35 | [UnaryExpr] !x | semmle.order | 1 |
| arrays.js:85:34:85:35 | [UnaryExpr] !x | arrays.js:85:35:85:35 | [VarRef] x | semmle.label | 1 |
| arrays.js:85:34:85:35 | [UnaryExpr] !x | arrays.js:85:35:85:35 | [VarRef] x | semmle.order | 1 |
| file://:0:0:0:0 | (Arguments) | arrays.js:5:8:5:14 | [DotExpr] obj.foo | semmle.label | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:5:8:5:14 | [DotExpr] obj.foo | semmle.order | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:8:12:8:17 | [VarRef] source | semmle.label | 0 |
@@ -1218,14 +1140,6 @@ edges
| file://:0:0:0:0 | (Arguments) | arrays.js:80:24:80:26 | [VarRef] arr | semmle.order | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:81:10:81:10 | [VarRef] x | semmle.label | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:81:10:81:10 | [VarRef] x | semmle.order | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:84:8:84:34 | [MethodCallExpr] ["sourc ... ) => x) | semmle.label | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:84:8:84:34 | [MethodCallExpr] ["sourc ... ) => x) | semmle.order | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:84:26:84:33 | [ArrowFunctionExpr] (x) => x | semmle.label | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:84:26:84:33 | [ArrowFunctionExpr] (x) => x | semmle.order | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:85:8:85:36 | [MethodCallExpr] ["sourc ... => !!x) | semmle.label | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:85:8:85:36 | [MethodCallExpr] ["sourc ... => !!x) | semmle.order | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:85:26:85:35 | [ArrowFunctionExpr] (x) => !!x | semmle.label | 0 |
| file://:0:0:0:0 | (Arguments) | arrays.js:85:26:85:35 | [ArrowFunctionExpr] (x) => !!x | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | arrays.js:15:16:15:16 | [SimpleParameter] e | semmle.label | 0 |
| file://:0:0:0:0 | (Parameters) | arrays.js:15:16:15:16 | [SimpleParameter] e | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | arrays.js:16:12:16:12 | [SimpleParameter] e | semmle.label | 0 |
@@ -1240,9 +1154,5 @@ edges
| file://:0:0:0:0 | (Parameters) | arrays.js:44:26:44:26 | [SimpleParameter] i | semmle.order | 1 |
| file://:0:0:0:0 | (Parameters) | arrays.js:44:29:44:31 | [SimpleParameter] ary | semmle.label | 2 |
| file://:0:0:0:0 | (Parameters) | arrays.js:44:29:44:31 | [SimpleParameter] ary | semmle.order | 2 |
| file://:0:0:0:0 | (Parameters) | arrays.js:84:27:84:27 | [SimpleParameter] x | semmle.label | 0 |
| file://:0:0:0:0 | (Parameters) | arrays.js:84:27:84:27 | [SimpleParameter] x | semmle.order | 0 |
| file://:0:0:0:0 | (Parameters) | arrays.js:85:27:85:27 | [SimpleParameter] x | semmle.label | 0 |
| file://:0:0:0:0 | (Parameters) | arrays.js:85:27:85:27 | [SimpleParameter] x | semmle.order | 0 |
graphProperties
| semmle.graphKind | tree |

View File

@@ -0,0 +1,38 @@
/**
* Provides predicates to guess whether the codebase is an application or a gem.
*/
private import ruby
/**
* Provides predicates to guess whether the codebase is an application or a gem.
*/
module Application {
/**
* Holds if the codebase has a .gemspec file.
* This indicates it is a gem, or contains a gem.
*/
private predicate hasGemspec() { exists(File f | f.getExtension() = "gemspec") }
/**
* Holds if the codebase has a Gemfile.
*/
private predicate hasGemfile() { exists(File f | f.getBaseName() = "Gemfile") }
/**
* Holds if the codebase has a Gemfile.lock
* This indicates it is probably an application (though some gems erroneously have this file, too).
*/
private predicate hasGemfileLock() { exists(File f | f.getBaseName() = "Gemfile.lock") }
/**
* Holds if the codebase is likely to be a gem.
* This is a heuristic, so may be wrong.
* In particular, it will be confused by applications that contain vendored gems.
*/
predicate isGem() {
hasGemspec()
or
hasGemfile() and not hasGemfileLock()
}
}

View File

@@ -24,8 +24,21 @@ class Expr extends Stmt, TExpr {
}
}
/** DEPRECATED: Use `SelfVariableAccess` instead. */
deprecated class Self = SelfVariableAccess;
/**
* A reference to the current object. For example:
* - `self == other`
* - `self.method_name`
* - `def self.method_name ... end`
*
* This also includes implicit references to the current object in method
* calls. For example, the method call `foo(123)` has an implicit `self`
* receiver, and is equivalent to the explicit `self.foo(123)`.
*/
class Self extends Expr, TSelf {
final override string getAPrimaryQlClass() { result = "Self" }
final override string toString() { result = "self" }
}
/**
* A sequence of expressions in the right-hand side of an assignment or

View File

@@ -201,16 +201,7 @@ class ClassVariableWriteAccess extends ClassVariableAccess, VariableWriteAccess
/** An access to a class variable where the value is read. */
class ClassVariableReadAccess extends ClassVariableAccess, VariableReadAccess { }
/**
* An access to the `self` variable. For example:
* - `self == other`
* - `self.method_name`
* - `def self.method_name ... end`
*
* This also includes implicit references to the current object in method
* calls. For example, the method call `foo(123)` has an implicit `self`
* receiver, and is equivalent to the explicit `self.foo(123)`.
*/
/** An access to the `self` variable */
class SelfVariableAccess extends LocalVariableAccess instanceof SelfVariableAccessImpl {
final override string getAPrimaryQlClass() { result = "SelfVariableAccess" }
}

View File

@@ -73,7 +73,7 @@ private module Cached {
m = resolveConstantReadAccess(c.getReceiver())
or
m = enclosingModule(c).getModule() and
c.getReceiver() instanceof SelfVariableAccess
c.getReceiver() instanceof Self
) and
result = resolveConstantReadAccess(c.getAnArgument())
}
@@ -437,7 +437,7 @@ private module ResolveImpl {
encl = enclosingModule(this) and
result = [qualifiedModuleNameNonRec(encl, _, _), qualifiedModuleNameRec(encl, _, _)]
|
this.getReceiver() instanceof SelfVariableAccess
this.getReceiver() instanceof Self
or
not exists(this.getReceiver())
)

View File

@@ -366,23 +366,7 @@ private module Cached {
cached
predicate isCapturedAccess(LocalVariableAccess access) {
exists(Scope scope1, Scope scope2 |
scope1 = access.getVariable().getDeclaringScope() and
scope2 = access.getCfgScope() and
scope1 != scope2
|
if access instanceof SelfVariableAccess
then
// ```
// class C
// def self.m // not a captured access
// end
// end
// ```
not scope2 instanceof Toplevel or
not access = any(SingletonMethod m).getObject()
else any()
)
access.getVariable().getDeclaringScope() != access.getCfgScope()
}
cached

View File

@@ -14,7 +14,7 @@ class EntryNode extends CfgNode, TEntryNode {
EntryNode() { this = TEntryNode(scope) }
final override EntryBasicBlock getBasicBlock() { result = super.getBasicBlock() }
final override EntryBasicBlock getBasicBlock() { result = CfgNode.super.getBasicBlock() }
final override Location getLocation() { result = scope.getLocation() }
@@ -31,7 +31,7 @@ class AnnotatedExitNode extends CfgNode, TAnnotatedExitNode {
/** Holds if this node represent a normal exit. */
final predicate isNormal() { normal = true }
final override AnnotatedExitBasicBlock getBasicBlock() { result = super.getBasicBlock() }
final override AnnotatedExitBasicBlock getBasicBlock() { result = CfgNode.super.getBasicBlock() }
final override Location getLocation() { result = scope.getLocation() }

View File

@@ -233,8 +233,6 @@ module Ssa {
)
}
override SelfVariable getSourceVariable() { result = v }
final override string toString() { result = "self (" + v.getDeclaringScope() + ")" }
final override Location getLocation() { result = this.getControlFlowNode().getLocation() }
@@ -316,7 +314,7 @@ module Ssa {
CapturedCallDefinition() {
exists(Variable v, BasicBlock bb, int i |
this.definesAt(v, bb, i) and
SsaImpl::capturedCallWrite(_, bb, i, v)
SsaImpl::capturedCallWrite(bb, i, v)
)
}

View File

@@ -203,7 +203,7 @@ private module Cached {
result = lookupMethod(tp, method) and
if result.(Method).isPrivate()
then
exists(SelfVariableAccess self |
exists(Self self |
self = call.getReceiver().getExpr() and
pragma[only_bind_out](self.getEnclosingModule().getModule().getSuperClass*()) =
pragma[only_bind_out](result.getEnclosingModule().getModule())
@@ -232,18 +232,6 @@ private module Cached {
)
}
/** Gets a viable run-time target for the call `call`. */
cached
DataFlowCallable viableCallable(DataFlowCall call) {
result = TCfgScope(getTarget(call.asCall())) and
not call.asCall().getExpr() instanceof YieldCall // handled by `lambdaCreation`/`lambdaCall`
or
exists(LibraryCallable callable |
result = TLibraryCallable(callable) and
call.asCall().getExpr() = callable.getACall()
)
}
cached
newtype TArgumentPosition =
TSelfArgumentPosition() or
@@ -312,14 +300,28 @@ private DataFlow::LocalSourceNode trackInstance(Module tp, TypeTracker t) {
)
or
// `self` in method
tp = result.(SsaSelfDefinitionNode).getSelfScope().(Method).getEnclosingModule().getModule()
exists(Self self, Method enclosing |
self = result.asExpr().getExpr() and
enclosing = self.getEnclosingMethod() and
tp = enclosing.getEnclosingModule().getModule() and
not self.getEnclosingModule().getEnclosingMethod() = enclosing
)
or
// `self` in singleton method
flowsToSingletonMethodObject(trackInstance(tp), result.(SsaSelfDefinitionNode).getSelfScope())
exists(Self self, MethodBase enclosing |
self = result.asExpr().getExpr() and
flowsToSingletonMethodObject(trackInstance(tp), enclosing) and
enclosing = self.getEnclosingMethod() and
not self.getEnclosingModule().getEnclosingMethod() = enclosing
)
or
// `self` in top-level
result.(SsaSelfDefinitionNode).getSelfScope() instanceof Toplevel and
tp = TResolved("Object")
exists(Self self, Toplevel enclosing |
self = result.asExpr().getExpr() and
enclosing = self.getEnclosingModule() and
tp = TResolved("Object") and
not self.getEnclosingMethod().getEnclosingModule() = enclosing
)
or
// a module or class
exists(Module m |
@@ -369,7 +371,7 @@ private predicate singletonMethod(MethodBase method, Expr object) {
pragma[nomagic]
private predicate flowsToSingletonMethodObject(DataFlow::LocalSourceNode nodeFrom, MethodBase method) {
exists(DataFlow::Node nodeTo |
exists(DataFlow::LocalSourceNode nodeTo |
nodeFrom.flowsTo(nodeTo) and
singletonMethod(method, nodeTo.asExpr().getExpr())
)
@@ -407,8 +409,13 @@ private DataFlow::LocalSourceNode trackSingletonMethod(MethodBase m, string name
name = m.getName()
}
private SsaSelfDefinitionNode selfInModule(Module tp) {
tp = result.getSelfScope().(ModuleBase).getModule()
private DataFlow::Node selfInModule(Module tp) {
exists(Self self, ModuleBase enclosing |
self = result.asExpr().getExpr() and
enclosing = self.getEnclosingModule() and
tp = enclosing.getModule() and
not self.getEnclosingMethod().getEnclosingModule() = enclosing
)
}
private DataFlow::LocalSourceNode trackModule(Module tp, TypeTracker t) {
@@ -435,6 +442,17 @@ private DataFlow::LocalSourceNode trackModule(Module tp) {
result = trackModule(tp, TypeTracker::end())
}
/** Gets a viable run-time target for the call `call`. */
DataFlowCallable viableCallable(DataFlowCall call) {
result = TCfgScope(getTarget(call.asCall())) and
not call.asCall().getExpr() instanceof YieldCall // handled by `lambdaCreation`/`lambdaCall`
or
exists(LibraryCallable callable |
result = TLibraryCallable(callable) and
call.asCall().getExpr() = callable.getACall()
)
}
/**
* Holds if the set of viable implementations that can be called by `call`
* might be improved by knowing the call context. This is the case if the

View File

@@ -70,20 +70,6 @@ module LocalFlow {
)
}
/** Gets the SSA definition node corresponding to the implicit `self` parameter for `m`. */
private SsaDefinitionNode getSelfParameterDefNode(MethodBase m) {
result.getDefinition().(Ssa::SelfDefinition).getSourceVariable().getDeclaringScope() = m
}
/**
* Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node.
*/
predicate localFlowSsaParamInput(Node nodeFrom, Node nodeTo) {
nodeTo = getParameterDefNode(nodeFrom.(ParameterNode).getParameter())
or
nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNode).getMethod())
}
/**
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
* involving SSA definition `def`.
@@ -129,6 +115,9 @@ module LocalFlow {
predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) {
localSsaFlowStep(nodeFrom, nodeTo)
or
nodeFrom.(SelfParameterNode).getMethod() = nodeTo.asExpr().getExpr().getEnclosingCallable() and
nodeTo.asExpr().getExpr() instanceof Self
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::AssignExprCfgNode).getRhs()
or
nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::BlockArgumentCfgNode).getValue()
@@ -247,7 +236,7 @@ private module Cached {
or
defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom)
or
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
nodeTo = LocalFlow::getParameterDefNode(nodeFrom.(ParameterNode).getParameter())
or
nodeTo.(SynthReturnNode).getAnInput() = nodeFrom
or
@@ -264,7 +253,7 @@ private module Cached {
or
defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom)
or
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
nodeTo = LocalFlow::getParameterDefNode(nodeFrom.(ParameterNode).getParameter())
or
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo)
or
@@ -286,34 +275,27 @@ private module Cached {
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo)
}
private predicate entrySsaDefinition(SsaDefinitionNode n) {
n = LocalFlow::getParameterDefNode(_)
or
exists(Ssa::Definition def | def = n.getDefinition() |
def instanceof Ssa::SelfDefinition
or
def instanceof Ssa::CapturedEntryDefinition
)
}
cached
predicate isLocalSourceNode(Node n) {
n instanceof ParameterNode
or
n instanceof PostUpdateNodes::ExprPostUpdateNode
// This case should not be needed once we have proper use-use flow
// for `self`. At that point, the `self`s returned by `trackInstance`
// in `DataFlowDispatch.qll` should refer to the post-update node,
// and we can remove this case.
n.asExpr().getExpr() instanceof Self
or
// Expressions that can't be reached from another entry definition or expression.
not localFlowStepTypeTracker+(any(Node n0 |
n0 instanceof ExprNode
// Nodes that can't be reached from another parameter or expression.
not localFlowStepTypeTracker+(any(Node e |
e instanceof ExprNode
or
entrySsaDefinition(n0)
), n.(ExprNode))
e instanceof ParameterNode
), n)
or
// Ensure all entry SSA definitions are local sources -- for parameters, this
// is needed by type tracking. Note that when the parameter has a default value,
// it will be reachable from an expression (the default value) and therefore
// won't be caught by the rule above.
entrySsaDefinition(n)
// Ensure all parameter SSA nodes are local sources -- this is needed by type tracking.
// Note that when the parameter has a default value, it will be reachable from an
// expression (the default value) and therefore won't be caught by the rule above.
n = LocalFlow::getParameterDefNode(_)
}
cached
@@ -376,16 +358,6 @@ class SsaDefinitionNode extends NodeImpl, TSsaDefinitionNode {
override string toStringImpl() { result = def.toString() }
}
/** An SSA definition for a `self` variable. */
class SsaSelfDefinitionNode extends LocalSourceNode, SsaDefinitionNode {
private SelfVariable self;
SsaSelfDefinitionNode() { self = def.getSourceVariable() }
/** Gets the scope in which the `self` variable is declared. */
Scope getSelfScope() { result = self.getDeclaringScope() }
}
/**
* A value returning statement, viewed as a node in a data flow graph.
*
@@ -773,6 +745,13 @@ predicate jumpStep(Node pred, Node succ) {
SsaImpl::captureFlowOut(pred.(SsaDefinitionNode).getDefinition(),
succ.(SsaDefinitionNode).getDefinition())
or
exists(Self s, Method m |
s = succ.asExpr().getExpr() and
pred.(SelfParameterNode).getMethod() = m and
m = s.getEnclosingMethod() and
m != s.getEnclosingCallable()
)
or
succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr()
}

View File

@@ -1,5 +1,4 @@
private import SsaImplCommon
private import SsaImplSpecific as SsaImplSpecific
private import codeql.ruby.AST
private import codeql.ruby.CFG
private import codeql.ruby.ast.Variable
@@ -41,50 +40,58 @@ private predicate capturedExitRead(AnnotatedExitBasicBlock bb, int i, LocalVaria
i = bb.length()
}
/**
* Holds if captured variable `v` is read directly inside `scope`,
* or inside a (transitively) nested scope of `scope`.
*/
private CfgScope getCaptureOuterCfgScope(CfgScope scope) {
result = scope.getOuterCfgScope() and
(
scope instanceof Block
or
scope instanceof Lambda
)
}
/** Holds if captured variable `v` is read inside `scope`. */
pragma[noinline]
private predicate hasCapturedRead(Variable v, CfgScope scope) {
any(LocalVariableReadAccess read |
read.getVariable() = v and scope = read.getCfgScope().getOuterCfgScope*()
read.getVariable() = v and scope = getCaptureOuterCfgScope*(read.getCfgScope())
).isCapturedAccess()
}
/**
* Holds if `v` is written inside basic block `bb`, which is in the immediate
* outer scope of `scope`.
*/
pragma[noinline]
private predicate variableWriteInOuterScope(BasicBlock bb, LocalVariable v, CfgScope scope) {
SsaImplSpecific::variableWrite(bb, _, v, _) and
scope.getOuterCfgScope() = bb.getScope()
}
pragma[noinline]
private predicate hasVariableWriteWithCapturedRead(BasicBlock bb, LocalVariable v, CfgScope scope) {
hasCapturedRead(v, scope) and
variableWriteInOuterScope(bb, v, scope)
exists(VariableWriteAccess write |
write = bb.getANode().getNode() and
write.getVariable() = v and
bb.getScope() = scope.getOuterCfgScope()
)
}
/**
* Holds if the call `call` at index `i` in basic block `bb` may reach
* a callable that reads captured variable `v`.
* Holds if the call at index `i` in basic block `bb` may reach a callable
* that reads captured variable `v`.
*/
private predicate capturedCallRead(Call call, BasicBlock bb, int i, LocalVariable v) {
private predicate capturedCallRead(BasicBlock bb, int i, LocalVariable v) {
exists(CfgScope scope |
hasVariableWriteWithCapturedRead(bb.getAPredecessor*(), v, scope) and
call = bb.getNode(i).getNode()
bb.getNode(i).getNode() instanceof Call
|
// If the read happens inside a block, we restrict to the call that
// contains the block
not scope instanceof Block
or
scope = call.(MethodCall).getBlock()
// If the read happens inside a block, we restrict to the call that
// contains the block
scope = any(MethodCall c | bb.getNode(i) = c.getAControlFlowNode()).getBlock()
)
}
/** Holds if captured variable `v` is written inside `scope`. */
pragma[noinline]
private predicate hasCapturedWrite(Variable v, CfgScope scope) {
any(LocalVariableWriteAccess write |
write.getVariable() = v and scope = getCaptureOuterCfgScope*(write.getCfgScope())
).isCapturedAccess()
}
/** Holds if `v` is read at index `i` in basic block `bb`. */
private predicate variableReadActual(BasicBlock bb, int i, LocalVariable v) {
exists(VariableReadAccess read |
@@ -97,38 +104,21 @@ predicate variableRead(BasicBlock bb, int i, LocalVariable v, boolean certain) {
variableReadActual(bb, i, v) and
certain = true
or
capturedCallRead(_, bb, i, v) and
capturedCallRead(bb, i, v) and
certain = false
or
capturedExitRead(bb, i, v) and
certain = false
}
/**
* Holds if captured variable `v` is written directly inside `scope`,
* or inside a (transitively) nested scope of `scope`.
*/
pragma[noinline]
private predicate hasCapturedWrite(Variable v, CfgScope scope) {
any(LocalVariableWriteAccess write |
write.getVariable() = v and scope = write.getCfgScope().getOuterCfgScope*()
).isCapturedAccess()
}
/**
* Holds if `v` is read inside basic block `bb`, which is in the immediate
* outer scope of `scope`.
*/
pragma[noinline]
private predicate variableReadActualInOuterScope(BasicBlock bb, LocalVariable v, CfgScope scope) {
variableReadActual(bb, _, v) and
bb.getScope() = scope.getOuterCfgScope()
}
pragma[noinline]
private predicate hasVariableReadWithCapturedWrite(BasicBlock bb, LocalVariable v, CfgScope scope) {
hasCapturedWrite(v, scope) and
variableReadActualInOuterScope(bb, v, scope)
exists(VariableReadAccess read |
read = bb.getANode().getNode() and
read.getVariable() = v and
bb.getScope() = scope.getOuterCfgScope()
)
}
cached
@@ -144,20 +134,20 @@ private module Cached {
}
/**
* Holds if the call `call` at index `i` in basic block `bb` may reach a callable
* Holds if the call at index `i` in basic block `bb` may reach a callable
* that writes captured variable `v`.
*/
cached
predicate capturedCallWrite(Call call, BasicBlock bb, int i, LocalVariable v) {
predicate capturedCallWrite(BasicBlock bb, int i, LocalVariable v) {
exists(CfgScope scope |
hasVariableReadWithCapturedWrite(bb.getASuccessor*(), v, scope) and
call = bb.getNode(i).getNode()
bb.getNode(i).getNode() instanceof Call
|
// If the write happens inside a block, we restrict to the call that
// contains the block
not scope instanceof Block
or
scope = call.(MethodCall).getBlock()
// If the write happens inside a block, we restrict to the call that
// contains the block
scope = any(MethodCall c | bb.getNode(i) = c.getAControlFlowNode()).getBlock()
)
}
@@ -187,26 +177,6 @@ private module Cached {
)
}
pragma[noinline]
private predicate defReachesCallReadInOuterScope(
Definition def, Call call, LocalVariable v, CfgScope scope
) {
exists(BasicBlock bb, int i |
ssaDefReachesRead(v, def, bb, i) and
capturedCallRead(call, bb, i, v) and
scope.getOuterCfgScope() = bb.getScope()
)
}
pragma[noinline]
private predicate hasCapturedEntryWrite(Definition entry, LocalVariable v, CfgScope scope) {
exists(BasicBlock bb, int i |
capturedEntryWrite(bb, i, v) and
entry.definesAt(v, bb, i) and
bb.getScope().getOuterCfgScope*() = scope
)
}
/**
* Holds if there is flow for a captured variable from the enclosing scope into a block.
* ```rb
@@ -218,35 +188,13 @@ private module Cached {
*/
cached
predicate captureFlowIn(Definition def, Definition entry) {
exists(Call call, LocalVariable v, CfgScope scope |
defReachesCallReadInOuterScope(def, call, v, scope) and
hasCapturedEntryWrite(entry, v, scope)
|
// If the read happens inside a block, we restrict to the call that
// contains the block
not scope instanceof Block
or
scope = call.(MethodCall).getBlock()
)
}
private import codeql.ruby.dataflow.SSA
pragma[noinline]
private predicate defReachesExitReadInInnerScope(Definition def, LocalVariable v, CfgScope scope) {
exists(BasicBlock bb, int i |
exists(LocalVariable v, BasicBlock bb, int i |
ssaDefReachesRead(v, def, bb, i) and
capturedExitRead(bb, i, v) and
scope = bb.getScope().getOuterCfgScope*()
)
}
pragma[noinline]
private predicate hasCapturedExitRead(Definition exit, Call call, LocalVariable v, CfgScope scope) {
exists(BasicBlock bb, int i |
capturedCallWrite(call, bb, i, v) and
exit.definesAt(v, bb, i) and
bb.getScope() = scope.getOuterCfgScope()
capturedCallRead(bb, i, v) and
exists(BasicBlock bb2, int i2 |
capturedEntryWrite(bb2, i2, v) and
entry.definesAt(v, bb2, i2)
)
)
}
@@ -262,15 +210,13 @@ private module Cached {
*/
cached
predicate captureFlowOut(Definition def, Definition exit) {
exists(Call call, LocalVariable v, CfgScope scope |
defReachesExitReadInInnerScope(def, v, scope) and
hasCapturedExitRead(exit, call, v, _)
|
// If the read happens inside a block, we restrict to the call that
// contains the block
not scope instanceof Block
or
scope = call.(MethodCall).getBlock()
exists(LocalVariable v, BasicBlock bb, int i |
ssaDefReachesRead(v, def, bb, i) and
capturedExitRead(bb, i, v) and
exists(BasicBlock bb2, int i2 |
capturedCallWrite(bb2, i2, v) and
exit.definesAt(v, bb2, i2)
)
)
}

View File

@@ -40,7 +40,7 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain)
) and
certain = true
or
SsaImpl::capturedCallWrite(_, bb, i, v) and
SsaImpl::capturedCallWrite(bb, i, v) and
certain = false
}

View File

@@ -66,53 +66,35 @@ private CfgNodes::ExprNodes::VariableWriteAccessCfgNode variablesInPattern(
)
}
/**
* Holds if the additional step from `nodeFrom` to `nodeTo` should be included
* in all global taint flow configurations.
*/
cached
private module Cached {
/**
* Holds if the additional step from `nodeFrom` to `nodeTo` should be included
* in all global taint flow configurations.
*/
cached
predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// value of `case` expression into variables in patterns
exists(CfgNodes::ExprNodes::CaseExprCfgNode case, CfgNodes::ExprNodes::InClauseCfgNode clause |
nodeFrom.asExpr() = case.getValue() and
clause = case.getBranch(_) and
nodeTo.(SsaDefinitionNode).getDefinition().getControlFlowNode() =
variablesInPattern(clause.getPattern())
)
or
// operation involving `nodeFrom`
exists(CfgNodes::ExprNodes::OperationCfgNode op |
op = nodeTo.asExpr() and
op.getAnOperand() = nodeFrom.asExpr() and
not op.getExpr() instanceof AssignExpr
)
or
// string interpolation of `nodeFrom` into `nodeTo`
nodeFrom.asExpr() =
nodeTo.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent()
or
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false)
or
// Although flow through arrays is modelled precisely using stores/reads, we still
// allow flow out of a _tainted_ array. This is needed in order to support taint-
// tracking configurations where the source is an array.
readStep(nodeFrom, any(DataFlow::Content::ArrayElementContent c), nodeTo)
}
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
cached
predicate localTaintStepCached(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
defaultAdditionalTaintStep(nodeFrom, nodeTo)
or
// Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural
FlowSummaryImpl::Private::Steps::summaryThroughStep(nodeFrom, nodeTo, false)
}
predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
// value of `case` expression into variables in patterns
exists(CfgNodes::ExprNodes::CaseExprCfgNode case, CfgNodes::ExprNodes::InClauseCfgNode clause |
nodeFrom.asExpr() = case.getValue() and
clause = case.getBranch(_) and
nodeTo.(SsaDefinitionNode).getDefinition().getControlFlowNode() =
variablesInPattern(clause.getPattern())
)
or
// operation involving `nodeFrom`
exists(CfgNodes::ExprNodes::OperationCfgNode op |
op = nodeTo.asExpr() and
op.getAnOperand() = nodeFrom.asExpr() and
not op.getExpr() instanceof AssignExpr
)
or
// string interpolation of `nodeFrom` into `nodeTo`
nodeFrom.asExpr() =
nodeTo.asExpr().(CfgNodes::ExprNodes::StringlikeLiteralCfgNode).getAComponent()
or
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false)
or
// Although flow through arrays is modelled precisely using stores/reads, we still
// allow flow out of a _tainted_ array. This is needed in order to support taint-
// tracking configurations where the source is an array.
readStep(nodeFrom, any(DataFlow::Content::ArrayElementContent c), nodeTo)
}
import Cached

View File

@@ -20,4 +20,14 @@ predicate localExprTaint(CfgNodes::ExprCfgNode e1, CfgNodes::ExprCfgNode e2) {
localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
}
predicate localTaintStep = localTaintStepCached/2;
/**
* Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
*/
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
defaultAdditionalTaintStep(nodeFrom, nodeTo)
or
// Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural
FlowSummaryImpl::Private::Steps::summaryThroughStep(nodeFrom, nodeTo, false)
}

View File

@@ -105,7 +105,7 @@ private class ActionControllerContextCall extends MethodCall {
private ActionControllerControllerClass controllerClass;
ActionControllerContextCall() {
this.getReceiver() instanceof SelfVariableAccess and
this.getReceiver() instanceof Self and
this.getEnclosingModule() = controllerClass
}

View File

@@ -61,7 +61,7 @@ private class ActionViewHtmlEscapeCall extends HtmlEscapeCall {
// A call in a context where some commonly used `ActionView` methods are available.
private class ActionViewContextCall extends MethodCall {
ActionViewContextCall() {
this.getReceiver() instanceof SelfVariableAccess and
this.getReceiver() instanceof Self and
inActionViewContext(this)
}

View File

@@ -7,7 +7,6 @@ private import codeql.ruby.Concepts
private import codeql.ruby.controlflow.CfgNodes
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.internal.DataFlowDispatch
private import codeql.ruby.dataflow.internal.DataFlowPrivate
private import codeql.ruby.ast.internal.Module
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.Stdlib
@@ -101,7 +100,7 @@ class ActiveRecordModelClassMethodCall extends MethodCall {
recvCls = this.getReceiver().(ActiveRecordModelClassMethodCall).getReceiverClass()
or
// e.g. self.where(...) within an ActiveRecordModelClass
this.getReceiver() instanceof SelfVariableAccess and
this.getReceiver() instanceof Self and
this.getEnclosingModule() = recvCls
}
@@ -284,15 +283,14 @@ private class ActiveRecordModelFinderCall extends ActiveRecordModelInstantiation
}
// A `self` reference that may resolve to an active record model object
private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation,
SsaSelfDefinitionNode {
private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation {
private ActiveRecordModelClass cls;
ActiveRecordModelClassSelfReference() {
exists(MethodBase m |
m = this.getCfgScope() and
m.getEnclosingModule() = cls and
m = cls.getAMethod()
exists(Self s |
s.getEnclosingModule() = cls and
s.getEnclosingMethod() = cls.getAMethod() and
s = this.asExpr().getExpr()
)
}

View File

@@ -221,7 +221,7 @@ private class GraphqlSchemaObjectClassMethodCall extends MethodCall {
recvCls.getModule() = resolveConstantReadAccess(this.getReceiver())
or
// e.g. self.some_method(...) within a graphql Object or Interface
this.getReceiver() instanceof SelfVariableAccess and
this.getReceiver() instanceof Self and
this.getEnclosingModule() = recvCls
}

View File

@@ -9,6 +9,8 @@ private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.FlowSummary
private import codeql.ruby.dataflow.internal.DataFlowDispatch
private import codeql.ruby.controlflow.CfgNodes
private import codeql.ruby.dataflow.SSA
/** Provides modeling for the `Kernel` class. */
module Kernel {
@@ -27,7 +29,7 @@ module Kernel {
or
methodCall instanceof UnknownMethodCall and
(
this.getReceiver().asExpr().getExpr() instanceof SelfVariableAccess and
this.getReceiver().asExpr().getExpr() instanceof Self and
isPrivateKernelMethod(methodCall.getMethodName())
or
isPublicKernelMethod(methodCall.getMethodName())
@@ -145,6 +147,43 @@ module Kernel {
}
}
/**
* A system command executed via the `Kernel.open` method.
* If the first argument passed to `Kernel.open` starts with "|" then the rest
* of the string will be interpreted as a shell command and executed.
* Ruby documentation: https://docs.ruby-lang.org/en/3.0.0/Kernel.html#method-i-open
*/
class KernelOpenCall extends SystemCommandExecution::Range, KernelMethodCall {
KernelOpenCall() { this.getMethodName() = "open" }
override DataFlow::Node getAnArgument() { result = this.getArgument(_) }
// Kernel.open invokes a subshell if the argument starts with "|".
// If we don't know the prefix of the argument, assume it could start with "|".
override predicate isShellInterpreted(DataFlow::Node arg) {
arg = this.getAnArgument() and
not stringArgumentCannotStartWithPipe(arg)
}
predicate stringArgumentCannotStartWithPipe(DataFlow::Node arg) {
// open("prefix#{expr}")
arg.asExpr()
.(ExprNodes::StringlikeLiteralCfgNode)
.getComponent(0)
.getConstantValue()
.getString()
.charAt(0) != "|"
or
// arg = "prefix#{expr}"
// open(arg)
exists(Ssa::WriteDefinition d, ExprNodes::StringlikeLiteralCfgNode s |
d.getARead() = arg.asExpr() and d.assigns(s)
|
s.getComponent(0).getConstantValue().getString().charAt(0) != "|"
)
}
}
/**
* A call to `Kernel.eval`, which executes its first argument as Ruby code.
* ```ruby

View File

@@ -4,11 +4,13 @@
* adding your own.
*/
private import ruby
private import codeql.ruby.DataFlow
private import codeql.ruby.dataflow.RemoteFlowSources
private import codeql.ruby.Concepts
private import codeql.ruby.Frameworks
private import codeql.ruby.ApiGraphs
private import codeql.ruby.ApplicationHeuristics
module CommandInjection {
/**
@@ -36,6 +38,19 @@ module CommandInjection {
override string getSourceType() { result = "a user-provided value" }
}
/**
* Input to a public method, considered as a flow source.
* This is only correct if the codebase is a Ruby gem, rather than an application.
*/
class LibraryInput extends Source {
LibraryInput() {
Application::isGem() and
exists(Method m | not m.isPrivate() | this.asParameter() = m.getAParameter())
}
override string getSourceType() { result = "user-provided input" }
}
/**
* A command argument to a function that initiates an operating system command.
*/

View File

@@ -88,7 +88,10 @@ predicate callStep(Node nodeFrom, Node nodeTo) {
// we model it as a call step, in order to avoid computing a potential
// self-cross product of all calls to a function that returns one of its parameters
// (only to later filter that flow out using `TypeTracker::append`).
DataFlowPrivate::LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
nodeTo =
DataFlowPrivate::LocalFlow::getParameterDefNode(nodeFrom
.(DataFlowPublic::ParameterNode)
.getParameter())
}
/**

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ calls/calls.rb:
# 58| getDesugared: [MethodCall] call to []
# 58| getReceiver: [ConstantReadAccess] Array
# 58| getArgument: [MethodCall] call to foo
# 58| getReceiver: [SelfVariableAccess] self
# 58| getReceiver: [Self, SelfVariableAccess] self
# 59| [ArrayLiteral] [...]
# 59| getDesugared: [MethodCall] call to []
# 59| getReceiver: [ConstantReadAccess] Array
@@ -15,7 +15,7 @@ calls/calls.rb:
# 66| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 66| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] var1
# 66| getAnOperand/getArgument/getRightOperand: [MethodCall] call to bar
# 66| getReceiver: [SelfVariableAccess] self
# 66| getReceiver: [Self, SelfVariableAccess] self
# 67| [AssignAddExpr] ... += ...
# 67| getDesugared: [AssignExpr] ... = ...
# 67| getAnOperand/getLeftOperand: [LocalVariableAccess] var1
@@ -32,9 +32,9 @@ calls/calls.rb:
# 226| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 226| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 227| getStmt: [MethodCall] call to baz
# 227| getReceiver: [SelfVariableAccess] self
# 227| getReceiver: [Self, SelfVariableAccess] self
# 226| getReceiver: [MethodCall] call to bar
# 226| getReceiver: [SelfVariableAccess] self
# 226| getReceiver: [Self, SelfVariableAccess] self
# 229| [ForExpr] for ... in ...
# 229| getDesugared: [MethodCall] call to each
# 229| getBlock: [BraceBlock] { ... }
@@ -52,9 +52,9 @@ calls/calls.rb:
# 249| getReceiver: [ConstantReadAccess] Hash
# 249| getArgument: [Pair] Pair
# 249| getKey: [MethodCall] call to foo
# 249| getReceiver: [SelfVariableAccess] self
# 249| getReceiver: [Self, SelfVariableAccess] self
# 249| getValue: [MethodCall] call to bar
# 249| getReceiver: [SelfVariableAccess] self
# 249| getReceiver: [Self, SelfVariableAccess] self
# 249| getArgument: [Pair] Pair
# 249| getKey: [MethodCall] call to foo
# 249| getReceiver: [ConstantReadAccess] X
@@ -63,7 +63,7 @@ calls/calls.rb:
# 314| [AssignExpr] ... = ...
# 314| getDesugared: [StmtSequence] ...
# 314| getStmt: [SetterMethodCall] call to foo=
# 314| getReceiver: [SelfVariableAccess] self
# 314| getReceiver: [Self, SelfVariableAccess] self
# 314| getArgument: [AssignExpr] ... = ...
# 314| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 314| getAnOperand/getRightOperand: [IntegerLiteral] 10
@@ -72,7 +72,7 @@ calls/calls.rb:
# 315| getDesugared: [StmtSequence] ...
# 315| getStmt: [SetterMethodCall] call to []=
# 315| getReceiver: [MethodCall] call to foo
# 315| getReceiver: [SelfVariableAccess] self
# 315| getReceiver: [Self, SelfVariableAccess] self
# 315| getArgument: [AssignExpr] ... = ...
# 315| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 315| getAnOperand/getRightOperand: [IntegerLiteral] 10
@@ -84,7 +84,7 @@ calls/calls.rb:
# 316| getAnOperand/getLeftOperand: [MethodCall] call to foo
# 316| getDesugared: [StmtSequence] ...
# 316| getStmt: [SetterMethodCall] call to foo=
# 316| getReceiver: [SelfVariableAccess] self
# 316| getReceiver: [Self, SelfVariableAccess] self
# 316| getArgument: [AssignExpr] ... = ...
# 316| getAnOperand/getRightOperand: [MethodCall] call to []
# 316| getArgument: [IntegerLiteral] 0
@@ -95,7 +95,7 @@ calls/calls.rb:
# 316| getAnOperand/getLeftOperand: [MethodCall] call to bar
# 316| getDesugared: [StmtSequence] ...
# 316| getStmt: [SetterMethodCall] call to bar=
# 316| getReceiver: [SelfVariableAccess] self
# 316| getReceiver: [Self, SelfVariableAccess] self
# 316| getArgument: [AssignExpr] ... = ...
# 316| getAnOperand/getRightOperand: [MethodCall] call to []
# 316| getArgument: [RangeLiteral] _ .. _
@@ -109,7 +109,7 @@ calls/calls.rb:
# 316| getDesugared: [StmtSequence] ...
# 316| getStmt: [SetterMethodCall] call to []=
# 316| getReceiver: [MethodCall] call to foo
# 316| getReceiver: [SelfVariableAccess] self
# 316| getReceiver: [Self, SelfVariableAccess] self
# 316| getArgument: [AssignExpr] ... = ...
# 316| getAnOperand/getRightOperand: [MethodCall] call to []
# 316| getArgument: [IntegerLiteral] -1
@@ -139,7 +139,7 @@ calls/calls.rb:
# 317| getDesugared: [StmtSequence] ...
# 317| getStmt: [SetterMethodCall] call to []=
# 317| getReceiver: [MethodCall] call to foo
# 317| getReceiver: [SelfVariableAccess] self
# 317| getReceiver: [Self, SelfVariableAccess] self
# 317| getArgument: [AssignExpr] ... = ...
# 317| getAnOperand/getRightOperand: [MethodCall] call to []
# 317| getArgument: [RangeLiteral] _ .. _
@@ -161,7 +161,7 @@ calls/calls.rb:
# 318| [AssignAddExpr] ... += ...
# 318| getDesugared: [StmtSequence] ...
# 318| getStmt: [AssignExpr] ... = ...
# 318| getAnOperand/getRightOperand: [SelfVariableAccess] self
# 318| getAnOperand/getRightOperand: [Self, SelfVariableAccess] self
# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 318| getStmt: [SetterMethodCall] call to count=
# 318| getReceiver: [LocalVariableAccess] __synth__0
@@ -177,7 +177,7 @@ calls/calls.rb:
# 319| getDesugared: [StmtSequence] ...
# 319| getStmt: [AssignExpr] ... = ...
# 319| getAnOperand/getRightOperand: [MethodCall] call to foo
# 319| getReceiver: [SelfVariableAccess] self
# 319| getReceiver: [Self, SelfVariableAccess] self
# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 319| getStmt: [SetterMethodCall] call to []=
# 319| getReceiver: [LocalVariableAccess] __synth__0
@@ -199,7 +199,7 @@ calls/calls.rb:
# 320| getStmt: [AssignExpr] ... = ...
# 320| getAnOperand/getRightOperand: [MethodCall] call to bar
# 320| getReceiver: [MethodCall] call to foo
# 320| getReceiver: [SelfVariableAccess] self
# 320| getReceiver: [Self, SelfVariableAccess] self
# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 320| getStmt: [SetterMethodCall] call to []=
# 320| getReceiver: [LocalVariableAccess] __synth__0
@@ -213,13 +213,13 @@ calls/calls.rb:
# 320| getStmt: [AssignExpr] ... = ...
# 320| getAnOperand/getRightOperand: [MethodCall] call to baz
# 320| getReceiver: [MethodCall] call to foo
# 320| getReceiver: [SelfVariableAccess] self
# 320| getReceiver: [Self, SelfVariableAccess] self
# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2
# 320| getStmt: [AssignExpr] ... = ...
# 320| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 320| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to boo
# 320| getReceiver: [MethodCall] call to foo
# 320| getReceiver: [SelfVariableAccess] self
# 320| getReceiver: [Self, SelfVariableAccess] self
# 320| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1
# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3
# 320| getStmt: [AssignExpr] ... = ...
@@ -260,7 +260,7 @@ calls/calls.rb:
# 340| getReceiver: [LocalVariableAccess] __synth__0__1
# 340| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...)
# 341| getStmt: [MethodCall] call to foo
# 341| getReceiver: [SelfVariableAccess] self
# 341| getReceiver: [Self, SelfVariableAccess] self
# 341| getArgument: [LocalVariableAccess] x
# 341| getArgument: [LocalVariableAccess] y
# 341| getArgument: [LocalVariableAccess] z
@@ -476,7 +476,7 @@ literals/literals.rb:
# 118| getValue: [IntegerLiteral] 7
# 118| getArgument: [HashSplatExpr] ** ...
# 118| getAnOperand/getOperand/getReceiver: [MethodCall] call to baz
# 118| getReceiver: [SelfVariableAccess] self
# 118| getReceiver: [Self, SelfVariableAccess] self
# 185| [HashLiteral] {...}
# 185| getDesugared: [MethodCall] call to []
# 185| getReceiver: [ConstantReadAccess] Hash

View File

@@ -1,6 +1,6 @@
| local_dataflow.rb:1:1:7:3 | self (foo) | local_dataflow.rb:3:8:3:10 | self |
| local_dataflow.rb:1:1:7:3 | self (local_dataflow.rb) | local_dataflow.rb:49:1:53:3 | self |
| local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:1:1:7:3 | self (foo) |
| local_dataflow.rb:1:1:7:3 | self in foo | local_dataflow.rb:3:8:3:10 | self |
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:1:9:1:9 | a |
| local_dataflow.rb:1:9:1:9 | a | local_dataflow.rb:2:7:2:7 | a |
| local_dataflow.rb:2:3:2:7 | ... = ... | local_dataflow.rb:3:13:3:13 | b |
@@ -71,7 +71,16 @@
| local_dataflow.rb:50:18:50:18 | x | local_dataflow.rb:51:20:51:20 | x |
| local_dataflow.rb:51:9:51:15 | "break" | local_dataflow.rb:51:3:51:15 | break |
| local_dataflow.rb:60:1:90:3 | self (test_case) | local_dataflow.rb:78:12:78:20 | self |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:60:1:90:3 | self (test_case) |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:78:12:78:20 | self |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:79:20:79:26 | self |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:80:24:80:30 | self |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:82:7:82:13 | self |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:83:7:83:13 | self |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:84:7:84:13 | self |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:85:22:85:28 | self |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:86:28:86:34 | self |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:87:20:87:26 | self |
| local_dataflow.rb:60:1:90:3 | self in test_case | local_dataflow.rb:89:3:89:9 | self |
| local_dataflow.rb:60:15:60:15 | x | local_dataflow.rb:60:15:60:15 | x |
| local_dataflow.rb:60:15:60:15 | x | local_dataflow.rb:61:12:61:12 | x |
| local_dataflow.rb:61:7:68:5 | case ... | local_dataflow.rb:61:3:68:5 | ... = ... |

View File

@@ -201,6 +201,7 @@ edges
| string_flow.rb:241:10:241:10 | b [array element] : | string_flow.rb:241:10:241:13 | ...[...] |
| string_flow.rb:242:10:242:10 | b [array element] : | string_flow.rb:242:10:242:13 | ...[...] |
| string_flow.rb:246:5:246:18 | ... = ... : | string_flow.rb:250:26:250:26 | a : |
| string_flow.rb:246:5:246:18 | ... = ... : | string_flow.rb:258:27:258:27 | a : |
| string_flow.rb:246:9:246:18 | call to source : | string_flow.rb:246:5:246:18 | ... = ... : |
| string_flow.rb:246:9:246:18 | call to source : | string_flow.rb:247:10:247:10 | a : |
| string_flow.rb:246:9:246:18 | call to source : | string_flow.rb:248:20:248:20 | a : |
@@ -214,6 +215,7 @@ edges
| string_flow.rb:250:26:250:26 | a : | string_flow.rb:250:10:250:28 | call to scrub |
| string_flow.rb:252:10:252:10 | a : | string_flow.rb:252:10:252:22 | call to scrub! |
| string_flow.rb:253:21:253:21 | a : | string_flow.rb:253:10:253:22 | call to scrub! |
| string_flow.rb:255:5:255:18 | ... = ... : | string_flow.rb:250:26:250:26 | a : |
| string_flow.rb:255:5:255:18 | ... = ... : | string_flow.rb:258:27:258:27 | a : |
| string_flow.rb:255:9:255:18 | call to source : | string_flow.rb:255:5:255:18 | ... = ... : |
| string_flow.rb:255:9:255:18 | call to source : | string_flow.rb:256:5:256:5 | a : |

View File

@@ -5,7 +5,6 @@ track
| type_tracker.rb:2:5:5:7 | return return in field= | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:2:5:5:7 | self (field=) | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:2:5:5:7 | self (field=) | type tracker without call steps | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:2:5:5:7 | self in field= | type tracker with call steps | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:2:5:5:7 | self in field= | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:2:5:5:7 | self in field= | type tracker without call steps | type_tracker.rb:2:5:5:7 | self in field= |
| type_tracker.rb:2:16:2:18 | val | type tracker with call steps | type_tracker.rb:2:16:2:18 | val |
@@ -17,10 +16,17 @@ track
| type_tracker.rb:3:9:3:23 | [post] self | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:3:9:3:23 | [post] self | type tracker without call steps | type_tracker.rb:3:9:3:23 | [post] self |
| type_tracker.rb:3:9:3:23 | call to puts | type tracker without call steps | type_tracker.rb:3:9:3:23 | call to puts |
| type_tracker.rb:3:9:3:23 | self | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:3:9:3:23 | self | type tracker without call steps | type_tracker.rb:3:9:3:23 | self |
| type_tracker.rb:3:14:3:17 | [post] self | type tracker without call steps | type_tracker.rb:3:14:3:17 | [post] self |
| type_tracker.rb:3:14:3:17 | self | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:3:14:3:17 | self | type tracker without call steps | type_tracker.rb:3:14:3:17 | self |
| type_tracker.rb:3:14:3:23 | [post] call to field | type tracker without call steps | type_tracker.rb:3:14:3:23 | [post] call to field |
| type_tracker.rb:3:14:3:23 | call to field | type tracker without call steps | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:4:9:4:14 | @field | type tracker without call steps | type_tracker.rb:4:9:4:14 | @field |
| type_tracker.rb:4:18:4:20 | val | type tracker without call steps | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:4:18:4:20 | val | type tracker without call steps | type_tracker.rb:4:18:4:20 | val |
| type_tracker.rb:4:18:4:20 | val | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:7:5:9:7 | &block | type tracker without call steps | type_tracker.rb:7:5:9:7 | &block |
| type_tracker.rb:7:5:9:7 | field | type tracker without call steps | type_tracker.rb:7:5:9:7 | field |
| type_tracker.rb:7:5:9:7 | return return in field | type tracker without call steps | type_tracker.rb:3:14:3:23 | call to field |
@@ -35,22 +41,15 @@ track
| type_tracker.rb:12:1:16:3 | m | type tracker without call steps | type_tracker.rb:12:1:16:3 | m |
| type_tracker.rb:12:1:16:3 | return return in m | type tracker without call steps | type_tracker.rb:12:1:16:3 | return return in m |
| type_tracker.rb:12:1:16:3 | self (m) | type tracker without call steps | type_tracker.rb:12:1:16:3 | self (m) |
| type_tracker.rb:12:1:16:3 | self in m | type tracker with call steps | type_tracker.rb:12:1:16:3 | self (m) |
| type_tracker.rb:12:1:16:3 | self in m | type tracker without call steps | type_tracker.rb:12:1:16:3 | self in m |
| type_tracker.rb:13:5:13:7 | var | type tracker without call steps | type_tracker.rb:13:5:13:7 | var |
| type_tracker.rb:13:5:13:23 | ... = ... | type tracker with call steps | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:13:5:13:23 | ... = ... | type tracker with call steps | type_tracker.rb:2:5:5:7 | self in field= |
| type_tracker.rb:13:5:13:23 | ... = ... | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:13:5:13:23 | ... = ... | type tracker without call steps | type_tracker.rb:13:5:13:23 | ... = ... |
| type_tracker.rb:13:11:13:19 | Container | type tracker without call steps | type_tracker.rb:13:11:13:19 | Container |
| type_tracker.rb:13:11:13:19 | [post] Container | type tracker without call steps | type_tracker.rb:13:11:13:19 | [post] Container |
| type_tracker.rb:13:11:13:23 | call to new | type tracker with call steps | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:13:11:13:23 | call to new | type tracker with call steps | type_tracker.rb:2:5:5:7 | self in field= |
| type_tracker.rb:13:11:13:23 | call to new | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:13:11:13:23 | call to new | type tracker without call steps | type_tracker.rb:13:11:13:23 | call to new |
| type_tracker.rb:14:5:14:7 | [post] var | type tracker with call steps | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:14:5:14:7 | [post] var | type tracker without call steps | type_tracker.rb:14:5:14:7 | [post] var |
| type_tracker.rb:14:5:14:13 | ... = ... | type tracker without call steps | type_tracker.rb:14:5:14:13 | ... = ... |
| type_tracker.rb:14:5:14:13 | [post] ... = ... | type tracker without call steps | type_tracker.rb:14:5:14:13 | [post] ... = ... |
| type_tracker.rb:14:5:14:13 | __synth__0 | type tracker without call steps | type_tracker.rb:14:5:14:13 | __synth__0 |
| type_tracker.rb:14:5:14:13 | call to field= | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
@@ -64,6 +63,7 @@ track
| type_tracker.rb:15:5:15:18 | [post] self | type tracker without call steps | type_tracker.rb:15:5:15:18 | [post] self |
| type_tracker.rb:15:5:15:18 | call to puts | type tracker without call steps | type_tracker.rb:12:1:16:3 | return return in m |
| type_tracker.rb:15:5:15:18 | call to puts | type tracker without call steps | type_tracker.rb:15:5:15:18 | call to puts |
| type_tracker.rb:15:5:15:18 | self | type tracker without call steps | type_tracker.rb:15:5:15:18 | self |
| type_tracker.rb:15:10:15:12 | [post] var | type tracker without call steps | type_tracker.rb:15:10:15:12 | [post] var |
| type_tracker.rb:15:10:15:18 | [post] call to field | type tracker without call steps | type_tracker.rb:15:10:15:18 | [post] call to field |
| type_tracker.rb:15:10:15:18 | call to field | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
@@ -76,7 +76,6 @@ trackEnd
| type_tracker.rb:2:5:5:7 | self (field=) | type_tracker.rb:3:9:3:23 | self |
| type_tracker.rb:2:5:5:7 | self (field=) | type_tracker.rb:3:14:3:17 | self |
| type_tracker.rb:2:5:5:7 | self (field=) | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:2:5:5:7 | self in field= |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:3:9:3:23 | self |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:3:14:3:17 | self |
@@ -95,10 +94,19 @@ trackEnd
| type_tracker.rb:3:9:3:23 | [post] self | type_tracker.rb:3:14:3:17 | self |
| type_tracker.rb:3:9:3:23 | [post] self | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:3:9:3:23 | call to puts | type_tracker.rb:3:9:3:23 | call to puts |
| type_tracker.rb:3:9:3:23 | self | type_tracker.rb:3:9:3:23 | self |
| type_tracker.rb:3:9:3:23 | self | type_tracker.rb:3:14:3:17 | self |
| type_tracker.rb:3:9:3:23 | self | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:3:14:3:17 | [post] self | type_tracker.rb:3:14:3:17 | [post] self |
| type_tracker.rb:3:14:3:17 | self | type_tracker.rb:3:14:3:17 | self |
| type_tracker.rb:3:14:3:17 | self | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:3:14:3:23 | [post] call to field | type_tracker.rb:3:14:3:23 | [post] call to field |
| type_tracker.rb:3:14:3:23 | call to field | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:4:9:4:14 | @field | type_tracker.rb:4:9:4:14 | @field |
| type_tracker.rb:4:18:4:20 | val | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:4:18:4:20 | val | type_tracker.rb:4:9:4:20 | ... = ... |
| type_tracker.rb:4:18:4:20 | val | type_tracker.rb:4:18:4:20 | val |
| type_tracker.rb:4:18:4:20 | val | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:7:5:9:7 | &block | type_tracker.rb:7:5:9:7 | &block |
| type_tracker.rb:7:5:9:7 | field | type_tracker.rb:1:1:10:3 | Container |
| type_tracker.rb:7:5:9:7 | field | type_tracker.rb:7:5:9:7 | field |
@@ -115,21 +123,11 @@ trackEnd
| type_tracker.rb:12:1:16:3 | return return in m | type_tracker.rb:12:1:16:3 | return return in m |
| type_tracker.rb:12:1:16:3 | self (m) | type_tracker.rb:12:1:16:3 | self (m) |
| type_tracker.rb:12:1:16:3 | self (m) | type_tracker.rb:15:5:15:18 | self |
| type_tracker.rb:12:1:16:3 | self in m | type_tracker.rb:12:1:16:3 | self (m) |
| type_tracker.rb:12:1:16:3 | self in m | type_tracker.rb:12:1:16:3 | self in m |
| type_tracker.rb:12:1:16:3 | self in m | type_tracker.rb:15:5:15:18 | self |
| type_tracker.rb:13:5:13:7 | var | type_tracker.rb:13:5:13:7 | var |
| type_tracker.rb:13:5:13:23 | ... = ... | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:13:5:13:23 | ... = ... | type_tracker.rb:2:5:5:7 | self in field= |
| type_tracker.rb:13:5:13:23 | ... = ... | type_tracker.rb:3:9:3:23 | self |
| type_tracker.rb:13:5:13:23 | ... = ... | type_tracker.rb:3:14:3:17 | self |
| type_tracker.rb:13:5:13:23 | ... = ... | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:13:5:13:23 | ... = ... | type_tracker.rb:13:5:13:23 | ... = ... |
| type_tracker.rb:13:5:13:23 | ... = ... | type_tracker.rb:14:5:14:7 | var |
| type_tracker.rb:13:5:13:23 | ... = ... | type_tracker.rb:15:10:15:12 | var |
| type_tracker.rb:13:11:13:19 | Container | type_tracker.rb:13:11:13:19 | Container |
| type_tracker.rb:13:11:13:19 | [post] Container | type_tracker.rb:13:11:13:19 | [post] Container |
| type_tracker.rb:13:11:13:23 | call to new | type_tracker.rb:2:5:5:7 | self (field=) |
| type_tracker.rb:13:11:13:23 | call to new | type_tracker.rb:2:5:5:7 | self in field= |
| type_tracker.rb:13:11:13:23 | call to new | type_tracker.rb:3:9:3:23 | self |
| type_tracker.rb:13:11:13:23 | call to new | type_tracker.rb:3:14:3:17 | self |
@@ -142,9 +140,6 @@ trackEnd
| type_tracker.rb:14:5:14:7 | [post] var | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:14:5:14:7 | [post] var | type_tracker.rb:14:5:14:7 | [post] var |
| type_tracker.rb:14:5:14:7 | [post] var | type_tracker.rb:15:10:15:12 | var |
| type_tracker.rb:14:5:14:13 | ... = ... | type_tracker.rb:14:5:14:13 | ... = ... |
| type_tracker.rb:14:5:14:13 | ... = ... | type_tracker.rb:14:5:14:13 | __synth__0 |
| type_tracker.rb:14:5:14:13 | ... = ... | type_tracker.rb:14:5:14:23 | ... |
| type_tracker.rb:14:5:14:13 | [post] ... = ... | type_tracker.rb:14:5:14:13 | [post] ... = ... |
| type_tracker.rb:14:5:14:13 | __synth__0 | type_tracker.rb:14:5:14:13 | __synth__0 |
| type_tracker.rb:14:5:14:13 | call to field= | type_tracker.rb:14:5:14:13 | call to field= |
@@ -162,6 +157,7 @@ trackEnd
| type_tracker.rb:15:5:15:18 | [post] self | type_tracker.rb:15:5:15:18 | [post] self |
| type_tracker.rb:15:5:15:18 | call to puts | type_tracker.rb:12:1:16:3 | return return in m |
| type_tracker.rb:15:5:15:18 | call to puts | type_tracker.rb:15:5:15:18 | call to puts |
| type_tracker.rb:15:5:15:18 | self | type_tracker.rb:15:5:15:18 | self |
| type_tracker.rb:15:10:15:12 | [post] var | type_tracker.rb:15:10:15:12 | [post] var |
| type_tracker.rb:15:10:15:18 | [post] call to field | type_tracker.rb:15:10:15:18 | [post] call to field |
| type_tracker.rb:15:10:15:18 | call to field | type_tracker.rb:15:10:15:18 | call to field |

View File

@@ -45,9 +45,9 @@ potentiallyUnsafeSqlExecutingMethodCall
| ActiveRecordInjection.rb:75:5:75:29 | call to order |
| ActiveRecordInjection.rb:80:7:80:40 | call to find_by |
activeRecordModelInstantiations
| ActiveRecordInjection.rb:8:3:11:5 | self (authenticate) | ActiveRecordInjection.rb:5:1:17:3 | User |
| ActiveRecordInjection.rb:10:5:10:68 | self | ActiveRecordInjection.rb:5:1:17:3 | User |
| ActiveRecordInjection.rb:15:5:15:40 | call to find_by | ActiveRecordInjection.rb:1:1:3:3 | UserGroup |
| ActiveRecordInjection.rb:20:3:24:5 | self (delete_by) | ActiveRecordInjection.rb:19:1:25:3 | Admin |
| ActiveRecordInjection.rb:23:5:23:25 | self | ActiveRecordInjection.rb:19:1:25:3 | Admin |
| ActiveRecordInjection.rb:80:7:80:40 | call to find_by | ActiveRecordInjection.rb:5:1:17:3 | User |
| ActiveRecordInjection.rb:85:5:85:33 | call to find_by | ActiveRecordInjection.rb:5:1:17:3 | User |
| ActiveRecordInjection.rb:88:5:88:34 | call to find | ActiveRecordInjection.rb:5:1:17:3 | User |

View File

@@ -37,6 +37,12 @@ kernelSpawnCallExecutions
| Kernel.rb:67:1:67:58 | call to spawn |
| Kernel.rb:68:1:68:61 | call to spawn |
| Kernel.rb:69:1:69:71 | call to spawn |
kernelOpenCallExecutions
| Kernel.rb:77:1:77:21 | call to open | Kernel.rb:77:6:77:20 | "\| cat foo.txt" |
| Kernel.rb:78:1:78:26 | call to open | Kernel.rb:78:6:78:20 | "\| cat foo.txt" |
| Kernel.rb:79:1:79:51 | call to open | Kernel.rb:79:6:79:20 | "\| cat foo.txt" |
| Kernel.rb:82:1:82:9 | call to open | Kernel.rb:82:6:82:8 | cmd |
| Kernel.rb:88:3:88:17 | call to open | Kernel.rb:88:8:88:16 | maybe_cmd |
sendCallCodeExecutions
| Kernel.rb:2:1:2:22 | call to send | Kernel.rb:2:6:2:12 | "raise" |
| Kernel.rb:5:1:5:19 | call to send | Kernel.rb:5:8:5:13 | "push" |

View File

@@ -7,6 +7,10 @@ query predicate kernelExecCallExecutions(KernelExecCall c) { any() }
query predicate kernelSpawnCallExecutions(KernelSpawnCall c) { any() }
query predicate kernelOpenCallExecutions(KernelOpenCall c, DataFlow::Node arg) {
c.isShellInterpreted(arg)
}
query DataFlow::Node sendCallCodeExecutions(SendCallCodeExecution e) { result = e.getCode() }
query DataFlow::Node evalCallCodeExecutions(EvalCallCodeExecution e) { result = e.getCode() }

View File

@@ -68,6 +68,29 @@ spawn({"FOO" => "BAR"}, "echo foo", unsetenv_others: true)
spawn({"FOO" => "BAR"}, "echo", "foo", unsetenv_others: true)
spawn({"FOO" => "BAR"}, ["echo", "echo"], "foo", unsetenv_others: true)
# GOOD
open("foo.txt")
open("foo.txt", "r")
open("foo.txt", "r") { |f| f.write("hello") }
# BAD
open("| cat foo.txt")
open("| cat foo.txt", "w")
open("| cat foo.txt", "w") { |f| f.write("hello") }
cmd = "| cat foo.txt"
open(cmd) # BAD
file = "foo.txt"
open(file) # GOOD
def open_wrapped(maybe_cmd)
open(maybe_cmd) # BAD - string could start with '|'
open("foo#{maybe_cmd}.txt") # GOOD - won't cause shell execution because string doesn't start with '|'
file = "foo#{maybe_cmd}.txt"
open(file) # GOOD - same as above
end
module MockSystem
def system(*args)
args

View File

@@ -29,14 +29,12 @@ definition
| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self |
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d |
| parameters.rb:1:1:1:1 | self (parameters.rb) | parameters.rb:1:1:62:1 | self |
| parameters.rb:1:9:5:3 | <captured> | parameters.rb:1:1:62:1 | self |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y |
| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self |
| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas |
| parameters.rb:15:1:19:3 | self (print_map) | parameters.rb:15:1:19:3 | self |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map |
| parameters.rb:16:12:18:5 | <captured> | parameters.rb:15:1:19:3 | self |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key |
@@ -112,7 +110,6 @@ definition
| ssa.rb:26:3:28:5 | <captured> | ssa.rb:25:1:30:3 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem |
| ssa.rb:32:1:36:3 | self (m3) | ssa.rb:32:1:36:3 | self |
| ssa.rb:33:16:35:5 | <captured> | ssa.rb:32:1:36:3 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self |