mirror of
https://github.com/github/codeql.git
synced 2026-04-23 15:55:18 +02:00
Merge branch 'main' into optionals2
This commit is contained in:
14
.github/workflows/compile-queries.yml
vendored
14
.github/workflows/compile-queries.yml
vendored
@@ -56,4 +56,16 @@ jobs:
|
||||
# do full compile if running on main - this populates the cache
|
||||
if : ${{ github.event_name != 'pull_request' }}
|
||||
shell: bash
|
||||
run: codeql query compile -j0 */ql/src --keep-going --warnings=error
|
||||
run: |
|
||||
# Move all the existing cache into another folder, so we only preserve the cache for the current queries.
|
||||
mkdir -p ${COMBINED_CACHE_DIR}
|
||||
rm */ql/src/.cache/{lock,size}
|
||||
# copy the contents of the .cache folders into the combined cache folder.
|
||||
cp -r */ql/src/.cache/* ${COMBINED_CACHE_DIR}/
|
||||
# clean up the .cache folders
|
||||
rm -rf */ql/src/.cache/*
|
||||
|
||||
# compile the queries
|
||||
codeql query compile -j0 */ql/src --keep-going --warnings=error --compilation-cache ${COMBINED_CACHE_DIR}
|
||||
env:
|
||||
COMBINED_CACHE_DIR: ${{ github.workspace }}/compilation-dir
|
||||
@@ -11,11 +11,12 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.FileSystem" Version="4.3.0"/>
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0"/>
|
||||
<PackageReference Include="xunit" Version="2.4.1"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Semmle.Autobuild.CSharp\Semmle.Autobuild.CSharp.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Autobuild.CSharp\Semmle.Autobuild.CSharp.csproj" />
|
||||
<ProjectReference Include="..\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -11,15 +11,15 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\"/>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="16.11.0"/>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extractor\Semmle.Util\Semmle.Util.csproj"/>
|
||||
<ProjectReference Include="..\..\extractor\Semmle.Extraction.CSharp\Semmle.Extraction.CSharp.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj"/>
|
||||
<ProjectReference Include="..\..\extractor\Semmle.Util\Semmle.Util.csproj" />
|
||||
<ProjectReference Include="..\..\extractor\Semmle.Extraction.CSharp\Semmle.Extraction.CSharp.csproj" />
|
||||
<ProjectReference Include="..\Semmle.Autobuild.Shared\Semmle.Autobuild.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -8,12 +8,12 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\"/>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="16.11.0"/>
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extractor\Semmle.Util\Semmle.Util.csproj"/>
|
||||
<ProjectReference Include="..\..\extractor\Semmle.Util\Semmle.Util.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
@@ -24,7 +24,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.DiaSymReader" Version="1.3.0" />
|
||||
<PackageReference Include="Microsoft.DiaSymReader" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.DiaSymReader.Native" Version="1.7.0" />
|
||||
<PackageReference Include="Microsoft.DiaSymReader.PortablePdb" Version="1.6.0"><IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -12,17 +12,17 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Semmle.Extraction.CSharp\Semmle.Extraction.CSharp.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Extraction.CSharp\Semmle.Extraction.CSharp.csproj" />
|
||||
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\"/>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="16.11.0"/>
|
||||
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0"/>
|
||||
<PackageReference Include="System.Net.Primitives" Version="4.3.1"/>
|
||||
<PackageReference Include="System.Security.Principal" Version="4.3.0"/>
|
||||
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0"/>
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="System.Net.Primitives" Version="4.3.1" />
|
||||
<PackageReference Include="System.Security.Principal" Version="4.3.0" />
|
||||
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.Entities
|
||||
{
|
||||
@@ -162,6 +163,13 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
operatorName = "false";
|
||||
break;
|
||||
default:
|
||||
var match = Regex.Match(methodName, "^op_Checked(.*)$");
|
||||
if (match.Success)
|
||||
{
|
||||
OperatorSymbol("op_" + match.Groups[1], out var uncheckedName);
|
||||
operatorName = "checked " + uncheckedName;
|
||||
break;
|
||||
}
|
||||
operatorName = methodName;
|
||||
success = false;
|
||||
break;
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Semmle.Extraction.CIL\Semmle.Extraction.CIL.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Extraction\Semmle.Extraction.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Extraction.CIL\Semmle.Extraction.CIL.csproj" />
|
||||
<ProjectReference Include="..\Semmle.Extraction\Semmle.Extraction.csproj" />
|
||||
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Properties\"/>
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1"/>
|
||||
<PackageReference Include="Microsoft.Build" Version="16.11.0"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -6,19 +6,19 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.FileSystem" Version="4.3.0"/>
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0"/>
|
||||
<PackageReference Include="xunit" Version="2.4.1"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Semmle.Extraction.CSharp.Standalone\Semmle.Extraction.CSharp.Standalone.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Extraction.CSharp\Semmle.Extraction.CSharp.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Extraction\Semmle.Extraction.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Extraction.CSharp.Standalone\Semmle.Extraction.CSharp.Standalone.csproj" />
|
||||
<ProjectReference Include="..\Semmle.Extraction.CSharp\Semmle.Extraction.CSharp.csproj" />
|
||||
<ProjectReference Include="..\Semmle.Extraction\Semmle.Extraction.csproj" />
|
||||
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -12,13 +12,13 @@
|
||||
<DefineConstants>TRACE;DEBUG;DEBUG_LABELS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.0.1"/>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.4.0" />
|
||||
<PackageReference Include="GitInfo" Version="2.2.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -6,14 +6,14 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="xunit" Version="2.4.1"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj"/>
|
||||
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,5 +1,6 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.internal.SsaImpl::Consistency
|
||||
import semmle.code.csharp.dataflow.internal.SsaImpl as Impl
|
||||
import Impl::Consistency
|
||||
import Ssa
|
||||
|
||||
class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition {
|
||||
@@ -10,6 +11,14 @@ class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition {
|
||||
}
|
||||
}
|
||||
|
||||
class MyRelevantDefinitionExt extends RelevantDefinitionExt, Impl::DefinitionExt {
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
query predicate localDeclWithSsaDef(LocalVariableDeclExpr d) {
|
||||
// Local variables in C# must be initialized before every use, so uninitialized
|
||||
// local variables should not have an SSA definition, as that would imply that
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -138,25 +138,6 @@ module Ssa {
|
||||
}
|
||||
}
|
||||
|
||||
private string getSplitString(Definition def) {
|
||||
exists(ControlFlow::BasicBlock bb, int i, ControlFlow::Node cfn |
|
||||
def.definesAt(_, bb, i) and
|
||||
result = cfn.(ControlFlow::Nodes::ElementNode).getSplitsString()
|
||||
|
|
||||
cfn = bb.getNode(i)
|
||||
or
|
||||
not exists(bb.getNode(i)) and
|
||||
cfn = bb.getFirstNode()
|
||||
)
|
||||
}
|
||||
|
||||
private string getToStringPrefix(Definition def) {
|
||||
result = "[" + getSplitString(def) + "] "
|
||||
or
|
||||
not exists(getSplitString(def)) and
|
||||
result = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* A static single assignment (SSA) definition. Either an explicit variable
|
||||
* definition (`ExplicitDefinition`), an implicit variable definition
|
||||
@@ -521,8 +502,8 @@ module Ssa {
|
||||
|
||||
override string toString() {
|
||||
if this.getADefinition() instanceof AssignableDefinitions::ImplicitParameterDefinition
|
||||
then result = getToStringPrefix(this) + "SSA param(" + this.getSourceVariable() + ")"
|
||||
else result = getToStringPrefix(this) + "SSA def(" + this.getSourceVariable() + ")"
|
||||
then result = SsaImpl::getToStringPrefix(this) + "SSA param(" + this.getSourceVariable() + ")"
|
||||
else result = SsaImpl::getToStringPrefix(this) + "SSA def(" + this.getSourceVariable() + ")"
|
||||
}
|
||||
|
||||
override Location getLocation() { result = ad.getLocation() }
|
||||
@@ -570,8 +551,12 @@ module Ssa {
|
||||
|
||||
override string toString() {
|
||||
if this.getSourceVariable().getAssignable() instanceof LocalScopeVariable
|
||||
then result = getToStringPrefix(this) + "SSA capture def(" + this.getSourceVariable() + ")"
|
||||
else result = getToStringPrefix(this) + "SSA entry def(" + this.getSourceVariable() + ")"
|
||||
then
|
||||
result =
|
||||
SsaImpl::getToStringPrefix(this) + "SSA capture def(" + this.getSourceVariable() + ")"
|
||||
else
|
||||
result =
|
||||
SsaImpl::getToStringPrefix(this) + "SSA entry def(" + this.getSourceVariable() + ")"
|
||||
}
|
||||
|
||||
override Location getLocation() { result = this.getCallable().getLocation() }
|
||||
@@ -612,7 +597,7 @@ module Ssa {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = getToStringPrefix(this) + "SSA call def(" + this.getSourceVariable() + ")"
|
||||
result = SsaImpl::getToStringPrefix(this) + "SSA call def(" + this.getSourceVariable() + ")"
|
||||
}
|
||||
|
||||
override Location getLocation() { result = this.getCall().getLocation() }
|
||||
@@ -640,7 +625,8 @@ module Ssa {
|
||||
final Definition getQualifierDefinition() { result = q }
|
||||
|
||||
override string toString() {
|
||||
result = getToStringPrefix(this) + "SSA qualifier def(" + this.getSourceVariable() + ")"
|
||||
result =
|
||||
SsaImpl::getToStringPrefix(this) + "SSA qualifier def(" + this.getSourceVariable() + ")"
|
||||
}
|
||||
|
||||
override Location getLocation() { result = this.getQualifierDefinition().getLocation() }
|
||||
@@ -682,7 +668,7 @@ module Ssa {
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = getToStringPrefix(this) + "SSA phi(" + this.getSourceVariable() + ")"
|
||||
result = SsaImpl::getToStringPrefix(this) + "SSA phi(" + this.getSourceVariable() + ")"
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -49,7 +49,23 @@ private module SsaInput implements SsaImplCommon::InputSig {
|
||||
}
|
||||
}
|
||||
|
||||
import SsaImplCommon::Make<SsaInput>
|
||||
private import SsaImplCommon::Make<SsaInput> as Impl
|
||||
|
||||
class Definition = Impl::Definition;
|
||||
|
||||
class WriteDefinition = Impl::WriteDefinition;
|
||||
|
||||
class UncertainWriteDefinition = Impl::UncertainWriteDefinition;
|
||||
|
||||
class PhiNode = Impl::PhiNode;
|
||||
|
||||
module Consistency = Impl::Consistency;
|
||||
|
||||
module ExposedForTestingOnly {
|
||||
predicate ssaDefReachesReadExt = Impl::ssaDefReachesReadExt/4;
|
||||
|
||||
predicate phiHasInputFromBlockExt = Impl::phiHasInputFromBlockExt/3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` reads source variable `v`.
|
||||
@@ -1072,7 +1088,7 @@ private predicate adjacentDefRead(
|
||||
Definition def, SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2,
|
||||
SsaInput::SourceVariable v
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
Impl::adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
v = def.getSourceVariable()
|
||||
}
|
||||
|
||||
@@ -1088,7 +1104,7 @@ private predicate adjacentDefReachesRead(
|
||||
exists(SsaInput::BasicBlock bb3, int i3 |
|
||||
adjacentDefReachesRead(def, bb1, i1, bb3, i3) and
|
||||
SsaInput::variableRead(bb3, i3, _, false) and
|
||||
adjacentDefRead(def, bb3, i3, bb2, i2)
|
||||
Impl::adjacentDefRead(def, bb3, i3, bb2, i2)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1111,11 +1127,11 @@ private predicate adjacentDefReachesUncertainRead(
|
||||
/** Same as `lastRefRedef`, but skips uncertain reads. */
|
||||
pragma[nomagic]
|
||||
private predicate lastRefSkipUncertainReads(Definition def, SsaInput::BasicBlock bb, int i) {
|
||||
lastRef(def, bb, i) and
|
||||
Impl::lastRef(def, bb, i) and
|
||||
not SsaInput::variableRead(bb, i, def.getSourceVariable(), false)
|
||||
or
|
||||
exists(SsaInput::BasicBlock bb0, int i0 |
|
||||
lastRef(def, bb0, i0) and
|
||||
Impl::lastRef(def, bb0, i0) and
|
||||
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
|
||||
)
|
||||
}
|
||||
@@ -1237,7 +1253,7 @@ private module Cached {
|
||||
v = def.getSourceVariable() and
|
||||
capturedReadIn(_, _, v, edef.getSourceVariable(), c, additionalCalls) and
|
||||
def = def0.getAnUltimateDefinition() and
|
||||
ssaDefReachesRead(_, def0, bb, i) and
|
||||
Impl::ssaDefReachesRead(_, def0, bb, i) and
|
||||
capturedReadIn(bb, i, v, _, _, _) and
|
||||
c = bb.getNode(i)
|
||||
)
|
||||
@@ -1264,18 +1280,18 @@ private module Cached {
|
||||
|
||||
cached
|
||||
predicate isLiveAtEndOfBlock(Definition def, ControlFlow::BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _)
|
||||
Impl::ssaDefReachesEndOfBlock(bb, def, _)
|
||||
}
|
||||
|
||||
cached
|
||||
Definition phiHasInputFromBlock(PhiNode phi, ControlFlow::BasicBlock bb) {
|
||||
phiHasInputFromBlock(phi, result, bb)
|
||||
Definition phiHasInputFromBlock(Ssa::PhiNode phi, ControlFlow::BasicBlock bb) {
|
||||
Impl::phiHasInputFromBlock(phi, result, bb)
|
||||
}
|
||||
|
||||
cached
|
||||
AssignableRead getAReadAtNode(Definition def, ControlFlow::Node cfn) {
|
||||
exists(Ssa::SourceVariable v, ControlFlow::BasicBlock bb, int i |
|
||||
ssaDefReachesRead(v, def, bb, i) and
|
||||
Impl::ssaDefReachesRead(v, def, bb, i) and
|
||||
variableReadActual(bb, i, v) and
|
||||
cfn = bb.getNode(i) and
|
||||
result.getAControlFlowNode() = cfn
|
||||
@@ -1313,11 +1329,11 @@ private module Cached {
|
||||
/** Same as `lastRefRedef`, but skips uncertain reads. */
|
||||
cached
|
||||
predicate lastRefBeforeRedef(Definition def, ControlFlow::BasicBlock bb, int i, Definition next) {
|
||||
lastRefRedef(def, bb, i, next) and
|
||||
Impl::lastRefRedef(def, bb, i, next) and
|
||||
not SsaInput::variableRead(bb, i, def.getSourceVariable(), false)
|
||||
or
|
||||
exists(SsaInput::BasicBlock bb0, int i0 |
|
||||
lastRefRedef(def, bb0, i0, next) and
|
||||
Impl::lastRefRedef(def, bb0, i0, next) and
|
||||
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
|
||||
)
|
||||
}
|
||||
@@ -1333,7 +1349,7 @@ private module Cached {
|
||||
|
||||
cached
|
||||
Definition uncertainWriteDefinitionInput(UncertainWriteDefinition def) {
|
||||
uncertainWriteDefinitionInput(def, result)
|
||||
Impl::uncertainWriteDefinitionInput(def, result)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -1343,10 +1359,57 @@ private module Cached {
|
||||
v = def.getSourceVariable() and
|
||||
p = v.getAssignable() and
|
||||
def = def0.getAnUltimateDefinition() and
|
||||
ssaDefReachesRead(_, def0, bb, i) and
|
||||
Impl::ssaDefReachesRead(_, def0, bb, i) and
|
||||
outRefExitRead(bb, i, v)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
private string getSplitString(DefinitionExt def) {
|
||||
exists(ControlFlow::BasicBlock bb, int i, ControlFlow::Node cfn |
|
||||
def.definesAt(_, bb, i, _) and
|
||||
result = cfn.(ControlFlow::Nodes::ElementNode).getSplitsString()
|
||||
|
|
||||
cfn = bb.getNode(i)
|
||||
or
|
||||
not exists(bb.getNode(i)) and
|
||||
cfn = bb.getFirstNode()
|
||||
)
|
||||
}
|
||||
|
||||
string getToStringPrefix(DefinitionExt def) {
|
||||
result = "[" + getSplitString(def) + "] "
|
||||
or
|
||||
not exists(getSplitString(def)) and
|
||||
result = ""
|
||||
}
|
||||
|
||||
/**
|
||||
* An extended static single assignment (SSA) definition.
|
||||
*
|
||||
* This is either a normal SSA definition (`Definition`) or a
|
||||
* phi-read node (`PhiReadNode`).
|
||||
*
|
||||
* Only intended for internal use.
|
||||
*/
|
||||
class DefinitionExt extends Impl::DefinitionExt {
|
||||
override string toString() { result = this.(Ssa::Definition).toString() }
|
||||
|
||||
/** Gets the location of this definition. */
|
||||
Location getLocation() { result = this.(Ssa::Definition).getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A phi-read node.
|
||||
*
|
||||
* Only intended for internal use.
|
||||
*/
|
||||
class PhiReadNode extends DefinitionExt, Impl::PhiReadNode {
|
||||
override string toString() {
|
||||
result = getToStringPrefix(this) + "SSA phi read(" + this.getSourceVariable() + ")"
|
||||
}
|
||||
|
||||
override Location getLocation() { result = this.getBasicBlock().getLocation() }
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ AnonymousObjectCreation.cs:
|
||||
# 7| 1: [TypeMention] AnonObj
|
||||
# 7| 1: [AssignExpr] ... = ...
|
||||
# 7| 0: [FieldAccess] access to field l
|
||||
# 7| 1: [ObjectCreation] object creation of type List<AnonObj>
|
||||
# 7| 1: [CastExpr] (...) ...
|
||||
# 7| 1: [ObjectCreation] object creation of type List<AnonObj>
|
||||
# 9| 6: [Property] Prop1
|
||||
# 9| -1: [TypeMention] int
|
||||
# 9| 3: [Getter] get_Prop1
|
||||
@@ -21,13 +22,15 @@ AnonymousObjectCreation.cs:
|
||||
# 13| 0: [ExprStmt] ...;
|
||||
# 13| 0: [MethodCall] call to method M1
|
||||
# 13| -1: [ThisAccess] this access
|
||||
# 13| 0: [ObjectCreation] object creation of type AnonObj
|
||||
# 13| -1: [ObjectInitializer] { ..., ... }
|
||||
# 13| 0: [MemberInitializer] ... = ...
|
||||
# 13| 0: [PropertyCall] access to property Prop1
|
||||
# 13| 1: [IntLiteral] 1
|
||||
# 13| 0: [CastExpr] (...) ...
|
||||
# 13| 1: [ObjectCreation] object creation of type AnonObj
|
||||
# 13| -1: [ObjectInitializer] { ..., ... }
|
||||
# 13| 0: [MemberInitializer] ... = ...
|
||||
# 13| 0: [PropertyCall] access to property Prop1
|
||||
# 13| 1: [IntLiteral] 1
|
||||
# 14| 1: [ReturnStmt] return ...;
|
||||
# 14| 0: [ObjectCreation] object creation of type AnonObj
|
||||
# 14| 0: [CastExpr] (...) ...
|
||||
# 14| 1: [ObjectCreation] object creation of type AnonObj
|
||||
# 17| 8: [DelegateType] D
|
||||
#-----| 2: (Parameters)
|
||||
# 17| 0: [Parameter] x
|
||||
@@ -42,9 +45,10 @@ AnonymousObjectCreation.cs:
|
||||
# 21| -1: [TypeMention] D
|
||||
# 21| 4: [BlockStmt] {...}
|
||||
# 21| 0: [ReturnStmt] return ...;
|
||||
# 21| 0: [ExplicitDelegateCreation] delegate creation of type D
|
||||
# 21| 0: [ImplicitDelegateCreation] delegate creation of type D
|
||||
# 21| 0: [MethodAccess] access to method M2
|
||||
# 21| 0: [CastExpr] (...) ...
|
||||
# 21| 1: [ExplicitDelegateCreation] delegate creation of type D
|
||||
# 21| 0: [ImplicitDelegateCreation] delegate creation of type D
|
||||
# 21| 0: [MethodAccess] access to method M2
|
||||
# 23| 11: [Method] MethodAdd
|
||||
# 23| -1: [TypeMention] Void
|
||||
# 24| 4: [BlockStmt] {...}
|
||||
@@ -53,7 +57,8 @@ AnonymousObjectCreation.cs:
|
||||
# 25| -1: [TypeMention] List<int>
|
||||
# 25| 1: [TypeMention] int
|
||||
# 25| 0: [LocalVariableAccess] access to local variable list
|
||||
# 25| 1: [ObjectCreation] object creation of type List<Int32>
|
||||
# 25| 1: [CastExpr] (...) ...
|
||||
# 25| 1: [ObjectCreation] object creation of type List<Int32>
|
||||
BinaryPattern.cs:
|
||||
# 3| [Class] BinaryPattern
|
||||
# 5| 5: [Property] P1
|
||||
|
||||
@@ -1,4 +1,30 @@
|
||||
| DefUse.cs:63:9:63:14 | this.Field2 | DefUse.cs:80:30:80:31 | access to local variable x1 |
|
||||
| Fields.cs:65:24:65:32 | this.LoopField | Fields.cs:63:16:63:28 | this access |
|
||||
| Properties.cs:65:24:65:31 | this.LoopProp | Properties.cs:63:16:63:16 | access to parameter i |
|
||||
| Test.cs:78:13:78:13 | x | Test.cs:90:9:97:9 | if (...) ... |
|
||||
phiReadNode
|
||||
| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:63:9:63:14 | this.Field2 |
|
||||
| Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:65:24:65:32 | this.LoopField |
|
||||
| Patterns.cs:20:9:38:9 | SSA phi read(o) | Patterns.cs:7:16:7:16 | o |
|
||||
| Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | Properties.cs:65:24:65:31 | this.LoopProp |
|
||||
| Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:8:13:8:13 | x |
|
||||
| Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:13 | x |
|
||||
| Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:78:13:78:13 | x |
|
||||
phiReadNodeRead
|
||||
| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:63:9:63:14 | this.Field2 | DefUse.cs:80:37:80:42 | access to field Field2 |
|
||||
| Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:65:24:65:32 | this.LoopField | Fields.cs:65:24:65:32 | access to field LoopField |
|
||||
| Patterns.cs:20:9:38:9 | SSA phi read(o) | Patterns.cs:7:16:7:16 | o | Patterns.cs:20:17:20:17 | access to local variable o |
|
||||
| Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | Properties.cs:65:24:65:31 | this.LoopProp | Properties.cs:65:24:65:31 | access to property LoopProp |
|
||||
| Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:8:13:8:13 | x | Test.cs:25:16:25:16 | access to local variable x |
|
||||
| Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:92:17:92:17 | access to local variable x |
|
||||
| Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:96:17:96:17 | access to local variable x |
|
||||
| Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:99:13:99:13 | access to local variable x |
|
||||
| Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:78:13:78:13 | x | Test.cs:104:17:104:17 | access to local variable x |
|
||||
phiReadInput
|
||||
| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:63:9:63:18 | SSA def(this.Field2) |
|
||||
| DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) | DefUse.cs:80:30:80:31 | SSA phi read(this.Field2) |
|
||||
| Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:61:17:61:17 | SSA entry def(this.LoopField) |
|
||||
| Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) | Fields.cs:63:16:63:28 | SSA phi read(this.LoopField) |
|
||||
| Patterns.cs:20:9:38:9 | SSA phi read(o) | Patterns.cs:7:16:7:23 | SSA def(o) |
|
||||
| Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | Properties.cs:61:17:61:17 | SSA entry def(this.LoopProp) |
|
||||
| Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) | Properties.cs:63:16:63:16 | SSA phi read(this.LoopProp) |
|
||||
| Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:24:9:24:15 | SSA phi(x) |
|
||||
| Test.cs:25:16:25:16 | SSA phi read(x) | Test.cs:25:16:25:16 | SSA phi read(x) |
|
||||
| Test.cs:90:9:97:9 | SSA phi read(x) | Test.cs:78:13:78:17 | SSA def(x) |
|
||||
| Test.cs:99:9:99:15 | SSA phi read(x) | Test.cs:90:9:97:9 | SSA phi read(x) |
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.internal.SsaImpl
|
||||
import ExposedForTestingOnly
|
||||
|
||||
from Ssa::SourceVariable v, ControlFlow::BasicBlock bb
|
||||
where phiReadExposedForTesting(bb, v)
|
||||
select v, bb
|
||||
query predicate phiReadNode(PhiReadNode phi, Ssa::SourceVariable v) { phi.getSourceVariable() = v }
|
||||
|
||||
query predicate phiReadNodeRead(PhiReadNode phi, Ssa::SourceVariable v, ControlFlow::Node read) {
|
||||
phi.getSourceVariable() = v and
|
||||
exists(ControlFlow::BasicBlock bb, int i |
|
||||
ssaDefReachesReadExt(v, phi, bb, i) and
|
||||
read = bb.getNode(i)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate phiReadInput(PhiReadNode phi, DefinitionExt inp) {
|
||||
phiHasInputFromBlockExt(phi, inp, _)
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class Test
|
||||
{
|
||||
use(x);
|
||||
}
|
||||
// no phi_use for `x`, because actual use exists in the block
|
||||
// phi_use for `x`, even though there is an actual use in the block
|
||||
use(x);
|
||||
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ private predicate foo(Expr e, Expr p) {
|
||||
1. Acronyms *should* use normal PascalCase/camelCase. However, two-letter acronyms should have both letters capitalized.
|
||||
1. Newtype predicate names *should* begin with `T`.
|
||||
1. Predicates that have a result *should* be named `get...`
|
||||
1. Predicates that can return multiple results *should* be named `getA...` or `getAn...`
|
||||
1. Predicates that can have multiple results *should* be named `getA...` or `getAn...`
|
||||
1. Predicates that don't have a result or parameters *should* be named `is...` or `has...`
|
||||
1. *Avoid* underscores in names.
|
||||
1. *Avoid* short or single-letter names for classes, predicates and fields.
|
||||
@@ -304,6 +304,7 @@ For more information about documenting the code that you contribute to this repo
|
||||
exists(Type arg | arg = this.getAChild() | arg instanceof TypeParameter)
|
||||
```
|
||||
|
||||
|
||||
```ql
|
||||
exists(Type qualifierType |
|
||||
this.hasNonExactQualifierType(qualifierType)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
The signature of `allowImplicitRead` on `DataFlow::Configuration` and `TaintTracking::Configuration` has changed from `allowImplicitRead(DataFlow::Node node, DataFlow::Content c)` to `allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c)`.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The `BarrierGuard` class has been deprecated. Such barriers and sanitizers can now instead be created using the new `BarrierGuard` parameterized module.
|
||||
@@ -124,10 +124,17 @@ predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
|
||||
/** Holds if `row` is a summary model. */
|
||||
predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||
|
||||
bindingset[input]
|
||||
private predicate getKind(string input, string kind, boolean generated) {
|
||||
input.splitAt(":", 0) = "generated" and kind = input.splitAt(":", 1) and generated = true
|
||||
or
|
||||
not input.matches("%:%") and kind = input and generated = false
|
||||
}
|
||||
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind
|
||||
string output, string kind, boolean generated
|
||||
) {
|
||||
exists(string row |
|
||||
sourceModel(row) and
|
||||
@@ -139,14 +146,14 @@ predicate sourceModel(
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = output and
|
||||
row.splitAt(";", 7) = kind
|
||||
exists(string k | row.splitAt(";", 7) = k and getKind(k, kind, generated))
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
predicate sinkModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind
|
||||
string input, string kind, boolean generated
|
||||
) {
|
||||
exists(string row |
|
||||
sinkModel(row) and
|
||||
@@ -158,22 +165,22 @@ predicate sinkModel(
|
||||
row.splitAt(";", 4) = signature and
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = kind
|
||||
exists(string k | row.splitAt(";", 7) = k and getKind(k, kind, generated))
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate summaryModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind
|
||||
string input, string output, string kind, boolean generated
|
||||
) {
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, _)
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, generated, _)
|
||||
}
|
||||
|
||||
/** Holds if a summary model `row` exists for the given parameters. */
|
||||
predicate summaryModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string row
|
||||
string input, string output, string kind, boolean generated, string row
|
||||
) {
|
||||
summaryModel(row) and
|
||||
row.splitAt(";", 0) = namespace and
|
||||
@@ -185,14 +192,14 @@ predicate summaryModel(
|
||||
row.splitAt(";", 5) = ext and
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = output and
|
||||
row.splitAt(";", 8) = kind
|
||||
exists(string k | row.splitAt(";", 8) = k and getKind(k, kind, generated))
|
||||
}
|
||||
|
||||
/** Holds if `package` have CSV framework coverage. */
|
||||
private predicate packageHasCsvCoverage(string package) {
|
||||
sourceModel(package, _, _, _, _, _, _, _) or
|
||||
sinkModel(package, _, _, _, _, _, _, _) or
|
||||
summaryModel(package, _, _, _, _, _, _, _, _)
|
||||
sourceModel(package, _, _, _, _, _, _, _, _) or
|
||||
sinkModel(package, _, _, _, _, _, _, _, _) or
|
||||
summaryModel(package, _, _, _, _, _, _, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,25 +241,25 @@ predicate modelCoverage(string package, int pkgs, string kind, string part, int
|
||||
part = "source" and
|
||||
n =
|
||||
strictcount(string subpkg, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string output |
|
||||
string ext, string output, boolean generated |
|
||||
canonicalPackageHasASubpackage(package, subpkg) and
|
||||
sourceModel(subpkg, type, subtypes, name, signature, ext, output, kind)
|
||||
sourceModel(subpkg, type, subtypes, name, signature, ext, output, kind, generated)
|
||||
)
|
||||
or
|
||||
part = "sink" and
|
||||
n =
|
||||
strictcount(string subpkg, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input |
|
||||
string ext, string input, boolean generated |
|
||||
canonicalPackageHasASubpackage(package, subpkg) and
|
||||
sinkModel(subpkg, type, subtypes, name, signature, ext, input, kind)
|
||||
sinkModel(subpkg, type, subtypes, name, signature, ext, input, kind, generated)
|
||||
)
|
||||
or
|
||||
part = "summary" and
|
||||
n =
|
||||
strictcount(string subpkg, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string output |
|
||||
string ext, string input, string output, boolean generated |
|
||||
canonicalPackageHasASubpackage(package, subpkg) and
|
||||
summaryModel(subpkg, type, subtypes, name, signature, ext, input, output, kind)
|
||||
summaryModel(subpkg, type, subtypes, name, signature, ext, input, output, kind, generated)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -261,9 +268,9 @@ predicate modelCoverage(string package, int pkgs, string kind, string part, int
|
||||
module CsvValidation {
|
||||
private string getInvalidModelInput() {
|
||||
exists(string pred, AccessPath input, string part |
|
||||
sinkModel(_, _, _, _, _, _, input, _) and pred = "sink"
|
||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, input, _, _) and pred = "summary"
|
||||
summaryModel(_, _, _, _, _, _, input, _, _, _) and pred = "summary"
|
||||
|
|
||||
(
|
||||
invalidSpecComponent(input, part) and
|
||||
@@ -279,9 +286,9 @@ module CsvValidation {
|
||||
|
||||
private string getInvalidModelOutput() {
|
||||
exists(string pred, string output, string part |
|
||||
sourceModel(_, _, _, _, _, _, output, _) and pred = "source"
|
||||
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, _, output, _) and pred = "summary"
|
||||
summaryModel(_, _, _, _, _, _, _, output, _, _) and pred = "summary"
|
||||
|
|
||||
invalidSpecComponent(output, part) and
|
||||
not part = "" and
|
||||
@@ -291,8 +298,9 @@ module CsvValidation {
|
||||
}
|
||||
|
||||
private string getInvalidModelKind() {
|
||||
exists(string row, string kind | summaryModel(row) |
|
||||
kind = row.splitAt(";", 8) and
|
||||
exists(string row, string k, string kind | summaryModel(row) |
|
||||
k = row.splitAt(";", 8) and
|
||||
getKind(k, kind, _) and
|
||||
not kind = ["taint", "value"] and
|
||||
result = "Invalid kind \"" + kind + "\" in summary model."
|
||||
)
|
||||
@@ -334,11 +342,11 @@ module CsvValidation {
|
||||
|
||||
private string getInvalidModelSignature() {
|
||||
exists(string pred, string namespace, string type, string name, string signature, string ext |
|
||||
sourceModel(namespace, type, _, name, signature, ext, _, _) and pred = "source"
|
||||
sourceModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "source"
|
||||
or
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _) and pred = "sink"
|
||||
sinkModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "sink"
|
||||
or
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "summary"
|
||||
summaryModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "summary"
|
||||
|
|
||||
not namespace.regexpMatch("[a-zA-Z0-9_\\./]*") and
|
||||
result = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
|
||||
@@ -371,9 +379,9 @@ pragma[nomagic]
|
||||
private predicate elementSpec(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
) {
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, _, _) or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _) or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _)
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _) or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
}
|
||||
|
||||
private string paramsStringPart(Function f, int i) {
|
||||
@@ -421,7 +429,9 @@ SourceOrSinkElement interpretElement(
|
||||
predicate hasExternalSpecification(Function f) {
|
||||
f = any(SummarizedCallable sc).asFunction()
|
||||
or
|
||||
exists(SourceOrSinkElement e | f = e.asEntity() | sourceElement(e, _, _) or sinkElement(e, _, _))
|
||||
exists(SourceOrSinkElement e | f = e.asEntity() |
|
||||
sourceElement(e, _, _, _) or sinkElement(e, _, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate parseField(AccessPathToken c, DataFlow::FieldContent f) {
|
||||
|
||||
@@ -6,6 +6,15 @@
|
||||
* (which does not use the shared data flow libraries).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convenience-predicate for extracting two capture groups at once.
|
||||
*/
|
||||
bindingset[input, regexp]
|
||||
private predicate regexpCaptureTwo(string input, string regexp, string capture1, string capture2) {
|
||||
capture1 = input.regexpCapture(regexp, 1) and
|
||||
capture2 = input.regexpCapture(regexp, 2)
|
||||
}
|
||||
|
||||
/** Companion module to the `AccessPath` class. */
|
||||
module AccessPath {
|
||||
/** A string that should be parsed as an access path. */
|
||||
@@ -13,6 +22,93 @@ module AccessPath {
|
||||
bindingset[this]
|
||||
Range() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an integer constant `n` or interval `n1..n2` (inclusive) and gets the value
|
||||
* of the constant or any value contained in the interval.
|
||||
*/
|
||||
bindingset[arg]
|
||||
int parseInt(string arg) {
|
||||
result = arg.toInt()
|
||||
or
|
||||
// Match "n1..n2"
|
||||
exists(string lo, string hi |
|
||||
regexpCaptureTwo(arg, "(-?\\d+)\\.\\.(-?\\d+)", lo, hi) and
|
||||
result = [lo.toInt() .. hi.toInt()]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a lower-bounded interval `n..` and gets the lower bound.
|
||||
*/
|
||||
bindingset[arg]
|
||||
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
|
||||
|
||||
/**
|
||||
* Parses an integer constant or interval (bounded or unbounded) that explicitly
|
||||
* references the arity, such as `N-1` or `N-3..N-1`.
|
||||
*
|
||||
* Note that expressions of form `N-x` will never resolve to a negative index,
|
||||
* even if `N` is zero (it will have no result in that case).
|
||||
*/
|
||||
bindingset[arg, arity]
|
||||
private int parseIntWithExplicitArity(string arg, int arity) {
|
||||
result >= 0 and // do not allow N-1 to resolve to a negative index
|
||||
exists(string lo |
|
||||
// N-x
|
||||
lo = arg.regexpCapture("N-(\\d+)", 1) and
|
||||
result = arity - lo.toInt()
|
||||
or
|
||||
// N-x..
|
||||
lo = arg.regexpCapture("N-(\\d+)\\.\\.", 1) and
|
||||
result = [arity - lo.toInt(), arity - 1]
|
||||
)
|
||||
or
|
||||
exists(string lo, string hi |
|
||||
// x..N-y
|
||||
regexpCaptureTwo(arg, "(-?\\d+)\\.\\.N-(\\d+)", lo, hi) and
|
||||
result = [lo.toInt() .. arity - hi.toInt()]
|
||||
or
|
||||
// N-x..N-y
|
||||
regexpCaptureTwo(arg, "N-(\\d+)\\.\\.N-(\\d+)", lo, hi) and
|
||||
result = [arity - lo.toInt() .. arity - hi.toInt()] and
|
||||
result >= 0
|
||||
or
|
||||
// N-x..y
|
||||
regexpCaptureTwo(arg, "N-(\\d+)\\.\\.(\\d+)", lo, hi) and
|
||||
result = [arity - lo.toInt() .. hi.toInt()] and
|
||||
result >= 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an integer constant or interval (bounded or unbounded) and gets any
|
||||
* of the integers contained within (of which there may be infinitely many).
|
||||
*
|
||||
* Has no result for arguments involving an explicit arity, such as `N-1`.
|
||||
*/
|
||||
bindingset[arg, result]
|
||||
int parseIntUnbounded(string arg) {
|
||||
result = parseInt(arg)
|
||||
or
|
||||
result >= parseLowerBound(arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an integer constant or interval (bounded or unbounded) that
|
||||
* may reference the arity of a call, such as `N-1` or `N-3..N-1`.
|
||||
*
|
||||
* Note that expressions of form `N-x` will never resolve to a negative index,
|
||||
* even if `N` is zero (it will have no result in that case).
|
||||
*/
|
||||
bindingset[arg, arity]
|
||||
int parseIntWithArity(string arg, int arity) {
|
||||
result = parseInt(arg)
|
||||
or
|
||||
result in [parseLowerBound(arg) .. arity - 1]
|
||||
or
|
||||
result = parseIntWithExplicitArity(arg, arity)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the `n`th token on the access path as a string. */
|
||||
@@ -53,7 +149,7 @@ class AccessPath extends string instanceof AccessPath::Range {
|
||||
* An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths.
|
||||
*/
|
||||
class AccessPathToken extends string {
|
||||
AccessPathToken() { this = getRawToken(any(AccessPath path), _) }
|
||||
AccessPathToken() { this = getRawToken(_, _) }
|
||||
|
||||
private string getPart(int part) {
|
||||
result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part)
|
||||
@@ -71,9 +167,16 @@ class AccessPathToken extends string {
|
||||
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
|
||||
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
|
||||
|
||||
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
|
||||
pragma[nomagic]
|
||||
string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) }
|
||||
|
||||
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
|
||||
string getAnArgument() { result = this.getArgument(_) }
|
||||
|
||||
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
|
||||
string getAnArgument(string name) { result = this.getArgument(name, _) }
|
||||
|
||||
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
|
||||
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
|
||||
}
|
||||
|
||||
@@ -108,3 +108,21 @@ predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable f) { non
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() }
|
||||
|
||||
private int parameterPosition() {
|
||||
result = [-1 .. any(DataFlowCallable c).getType().getNumParameter()]
|
||||
}
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition extends int {
|
||||
ParameterPosition() { this = parameterPosition() }
|
||||
}
|
||||
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition extends int {
|
||||
ArgumentPosition() { this = parameterPosition() }
|
||||
}
|
||||
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,17 @@ private import DataFlowImplSpecific::Public
|
||||
import Cached
|
||||
|
||||
module DataFlowImplCommonPublic {
|
||||
/** A state value to track during data flow. */
|
||||
class FlowState = string;
|
||||
|
||||
/**
|
||||
* The default state, which is used when the state is unspecified for a source
|
||||
* or a sink.
|
||||
*/
|
||||
class FlowStateEmpty extends FlowState {
|
||||
FlowStateEmpty() { this = "" }
|
||||
}
|
||||
|
||||
private newtype TFlowFeature =
|
||||
TFeatureHasSourceCallContext() or
|
||||
TFeatureHasSinkCallContext() or
|
||||
@@ -62,6 +73,18 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
|
||||
tupleLimit = 1000
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is an argument of `call` with an argument position that matches
|
||||
* parameter position `ppos`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) {
|
||||
exists(ArgumentPosition apos |
|
||||
arg.argumentOf(call, apos) and
|
||||
parameterMatch(ppos, apos)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a simple data-flow analysis for resolving lambda calls. The analysis
|
||||
* currently excludes read-steps, store-steps, and flow-through.
|
||||
@@ -71,25 +94,27 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
|
||||
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
|
||||
*/
|
||||
private module LambdaFlow {
|
||||
private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) {
|
||||
p.isParameterOf(viableCallable(call), i)
|
||||
pragma[noinline]
|
||||
private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) {
|
||||
p.isParameterOf(viableCallable(call), ppos)
|
||||
}
|
||||
|
||||
private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) {
|
||||
p.isParameterOf(viableCallableLambda(call, _), i)
|
||||
pragma[noinline]
|
||||
private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) {
|
||||
p.isParameterOf(viableCallableLambda(call, _), ppos)
|
||||
}
|
||||
|
||||
private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
|
||||
exists(int i |
|
||||
viableParamNonLambda(call, i, p) and
|
||||
arg.argumentOf(call, i)
|
||||
exists(ParameterPosition ppos |
|
||||
viableParamNonLambda(call, ppos, p) and
|
||||
argumentPositionMatch(call, arg, ppos)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
|
||||
exists(int i |
|
||||
viableParamLambda(call, i, p) and
|
||||
arg.argumentOf(call, i)
|
||||
exists(ParameterPosition ppos |
|
||||
viableParamLambda(call, ppos, p) and
|
||||
argumentPositionMatch(call, arg, ppos)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -191,10 +216,9 @@ private module LambdaFlow {
|
||||
or
|
||||
// jump step
|
||||
exists(Node mid, DataFlowType t0 |
|
||||
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, _) and
|
||||
revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and
|
||||
toReturn = false and
|
||||
toJump = true and
|
||||
lastCall = TDataFlowCallNone()
|
||||
toJump = true
|
||||
|
|
||||
jumpStepCached(node, mid) and
|
||||
t = t0
|
||||
@@ -301,7 +325,10 @@ private module Cached {
|
||||
predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) }
|
||||
|
||||
cached
|
||||
predicate clearsContentCached(Node n, Content c) { clearsContent(n, c) }
|
||||
predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) }
|
||||
|
||||
cached
|
||||
predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) }
|
||||
|
||||
cached
|
||||
predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }
|
||||
@@ -322,7 +349,7 @@ private module Cached {
|
||||
or
|
||||
exists(ArgNode arg |
|
||||
result.(PostUpdateNode).getPreUpdateNode() = arg and
|
||||
arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition())
|
||||
arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -330,7 +357,7 @@ private module Cached {
|
||||
predicate returnNodeExt(Node n, ReturnKindExt k) {
|
||||
k = TValueReturn(n.(ReturnNode).getKind())
|
||||
or
|
||||
exists(ParamNode p, int pos |
|
||||
exists(ParamNode p, ParameterPosition pos |
|
||||
parameterValueFlowsToPreUpdate(p, n) and
|
||||
p.isParameterOf(_, pos) and
|
||||
k = TParamUpdate(pos)
|
||||
@@ -348,15 +375,17 @@ private module Cached {
|
||||
// For reads, `x.f`, we want to check that the tracked type after the read (which
|
||||
// is obtained by popping the head of the access path stack) is compatible with
|
||||
// the type of `x.f`.
|
||||
read(_, _, n)
|
||||
readSet(_, _, n)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) }
|
||||
predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) {
|
||||
isParameterNode(p, c, pos)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate argumentNode(Node n, DataFlowCall call, int pos) {
|
||||
n.(ArgumentNode).argumentOf(call, pos)
|
||||
predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) {
|
||||
isArgumentNode(n, call, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,12 +403,12 @@ private module Cached {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
|
||||
* The instance parameter is considered to have index `-1`.
|
||||
* Holds if `p` is the parameter of a viable dispatch target of `call`,
|
||||
* and `p` has position `ppos`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate viableParam(DataFlowCall call, int i, ParamNode p) {
|
||||
p.isParameterOf(viableCallableExt(call), i)
|
||||
private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) {
|
||||
p.isParameterOf(viableCallableExt(call), ppos)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,9 +417,9 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) {
|
||||
exists(int i |
|
||||
viableParam(call, i, p) and
|
||||
arg.argumentOf(call, i) and
|
||||
exists(ParameterPosition ppos |
|
||||
viableParam(call, ppos, p) and
|
||||
argumentPositionMatch(call, arg, ppos) and
|
||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p))
|
||||
)
|
||||
}
|
||||
@@ -442,7 +471,7 @@ private module Cached {
|
||||
// read
|
||||
exists(Node mid |
|
||||
parameterValueFlowCand(p, mid, false) and
|
||||
read(mid, _, node) and
|
||||
readSet(mid, _, node) and
|
||||
read = true
|
||||
)
|
||||
or
|
||||
@@ -630,8 +659,10 @@ private module Cached {
|
||||
* Holds if `arg` flows to `out` through a call using only
|
||||
* value-preserving steps and a single read step, not taking call
|
||||
* contexts into account, thus representing a getter-step.
|
||||
*
|
||||
* This predicate is exposed for testing only.
|
||||
*/
|
||||
predicate getterStep(ArgNode arg, Content c, Node out) {
|
||||
predicate getterStep(ArgNode arg, ContentSet c, Node out) {
|
||||
argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out)
|
||||
}
|
||||
|
||||
@@ -754,8 +785,12 @@ private module Cached {
|
||||
parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone())
|
||||
}
|
||||
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
cached
|
||||
predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) }
|
||||
|
||||
cached
|
||||
predicate storeSet(
|
||||
Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
storeStep(node1, c, node2) and
|
||||
contentType = getNodeDataFlowType(node1) and
|
||||
@@ -767,14 +802,19 @@ private module Cached {
|
||||
|
|
||||
argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1)
|
||||
or
|
||||
read(n2, c, n1) and
|
||||
readSet(n2, c, n1) and
|
||||
contentType = getNodeDataFlowType(n1) and
|
||||
containerType = getNodeDataFlowType(n2)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate read(Node node1, Content c, Node node2) { readStep(node1, c, node2) }
|
||||
private predicate store(
|
||||
Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType
|
||||
) {
|
||||
exists(ContentSet cs |
|
||||
c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a direct assignment to
|
||||
@@ -862,7 +902,7 @@ private module Cached {
|
||||
cached
|
||||
newtype TReturnKindExt =
|
||||
TValueReturn(ReturnKind kind) or
|
||||
TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) }
|
||||
TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) }
|
||||
|
||||
cached
|
||||
newtype TBooleanOption =
|
||||
@@ -905,16 +945,16 @@ class CastingNode extends Node {
|
||||
}
|
||||
|
||||
private predicate readStepWithTypes(
|
||||
Node n1, DataFlowType container, Content c, Node n2, DataFlowType content
|
||||
Node n1, DataFlowType container, ContentSet c, Node n2, DataFlowType content
|
||||
) {
|
||||
read(n1, c, n2) and
|
||||
readSet(n1, c, n2) and
|
||||
container = getNodeDataFlowType(n1) and
|
||||
content = getNodeDataFlowType(n2)
|
||||
}
|
||||
|
||||
private newtype TReadStepTypesOption =
|
||||
TReadStepTypesNone() or
|
||||
TReadStepTypesSome(DataFlowType container, Content c, DataFlowType content) {
|
||||
TReadStepTypesSome(DataFlowType container, ContentSet c, DataFlowType content) {
|
||||
readStepWithTypes(_, container, c, _, content)
|
||||
}
|
||||
|
||||
@@ -923,7 +963,7 @@ private class ReadStepTypesOption extends TReadStepTypesOption {
|
||||
|
||||
DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) }
|
||||
|
||||
Content getContent() { this = TReadStepTypesSome(_, result, _) }
|
||||
ContentSet getContent() { this = TReadStepTypesSome(_, result, _) }
|
||||
|
||||
DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) }
|
||||
|
||||
@@ -1054,9 +1094,9 @@ class ParamNode extends Node {
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of callable `c` at the specified
|
||||
* (zero-based) position.
|
||||
* position.
|
||||
*/
|
||||
predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) }
|
||||
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) }
|
||||
}
|
||||
|
||||
/** A data-flow node that represents a call argument. */
|
||||
@@ -1064,7 +1104,9 @@ class ArgNode extends Node {
|
||||
ArgNode() { argumentNode(this, _, _) }
|
||||
|
||||
/** Holds if this argument occurs at the given position in the given call. */
|
||||
final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) }
|
||||
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
argumentNode(this, call, pos)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1110,11 +1152,14 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn {
|
||||
}
|
||||
|
||||
class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
|
||||
private int pos;
|
||||
private ParameterPosition pos;
|
||||
|
||||
ParamUpdateReturnKind() { this = TParamUpdate(pos) }
|
||||
|
||||
int getPosition() { result = pos }
|
||||
ParameterPosition getPosition() { result = pos }
|
||||
|
||||
pragma[nomagic]
|
||||
ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) }
|
||||
|
||||
override string toString() { result = "param update " + pos }
|
||||
}
|
||||
@@ -1258,7 +1303,7 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** Content tagged with the type of a containing object. */
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
private DataFlowType t;
|
||||
@@ -1293,8 +1338,6 @@ abstract class AccessPathFront extends TAccessPathFront {
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContent getHead() { this = TFrontHead(result) }
|
||||
|
||||
predicate isClearedAt(Node n) { clearsContentCached(n, this.getHead().getContent()) }
|
||||
}
|
||||
|
||||
class AccessPathFrontNil extends AccessPathFront, TFrontNil {
|
||||
|
||||
@@ -12,7 +12,7 @@ private newtype TNode =
|
||||
MkGlobalFunctionNode(Function f) or
|
||||
MkSummarizedParameterNode(DataFlowCallable c, int i) {
|
||||
not exists(c.getFuncDef()) and
|
||||
c instanceof SummarizedCallable and
|
||||
c.asCallable() instanceof SummarizedCallable and
|
||||
(
|
||||
i in [0 .. c.getType().getNumParameter() - 1]
|
||||
or
|
||||
@@ -25,6 +25,8 @@ private newtype TNode =
|
||||
|
||||
/** Nodes intended for only use inside the data-flow libraries. */
|
||||
module Private {
|
||||
private import DataFlowDispatch
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n) {
|
||||
result.asCallable() = n.getEnclosingCallable()
|
||||
@@ -33,10 +35,15 @@ module Private {
|
||||
}
|
||||
|
||||
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
|
||||
predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) {
|
||||
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
|
||||
p.isParameterOf(c.asCallable(), pos)
|
||||
}
|
||||
|
||||
/** Holds if `arg` is an `ArgumentNode` of `c` with position `pos`. */
|
||||
predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos) {
|
||||
arg.argumentOf(c, pos)
|
||||
}
|
||||
|
||||
/** A data flow node that represents returning a value from a function. */
|
||||
class ReturnNode extends Node {
|
||||
ReturnKind kind;
|
||||
@@ -115,7 +122,7 @@ module Public {
|
||||
exists(DataFlowCallable dfc | result = dfc.asCallable() |
|
||||
this = MkSummarizedParameterNode(dfc, _)
|
||||
or
|
||||
this = MkSummaryInternalNode(dfc, _)
|
||||
this = MkSummaryInternalNode(dfc.asCallable(), _)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -188,6 +188,14 @@ predicate clearsContent(Node n, Content c) {
|
||||
// FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value that is being tracked is expected to be stored inside content `c`
|
||||
* at node `n`.
|
||||
*/
|
||||
predicate expectsContent(Node n, ContentSet c) {
|
||||
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n, c)
|
||||
}
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
DataFlowType getNodeType(Node n) {
|
||||
result = n.getType()
|
||||
|
||||
@@ -108,7 +108,7 @@ predicate localFlowStep(Node nodeFrom, Node 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, true)
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,6 +131,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
private newtype TContent =
|
||||
@@ -160,8 +161,10 @@ class Content extends TContent {
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +233,36 @@ class SyntheticFieldContent extends Content, TSyntheticFieldContent {
|
||||
override string toString() { result = s.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An entity that represents a set of `Content`s.
|
||||
*
|
||||
* The set may be interpreted differently depending on whether it is
|
||||
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
|
||||
*/
|
||||
class ContentSet instanceof Content {
|
||||
/** Gets a content that may be stored into when storing into this set. */
|
||||
Content getAStoreContent() { result = this }
|
||||
|
||||
/** Gets a content that may be read from when reading from this set. */
|
||||
Content getAReadContent() { result = this }
|
||||
|
||||
/** Gets a textual representation of this content set. */
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
|
||||
*
|
||||
|
||||
@@ -24,7 +24,11 @@ module Public {
|
||||
class SummaryComponent extends TSummaryComponent {
|
||||
/** Gets a textual representation of this summary component. */
|
||||
string toString() {
|
||||
exists(Content c | this = TContentSummaryComponent(c) and result = c.toString())
|
||||
exists(ContentSet c | this = TContentSummaryComponent(c) and result = c.toString())
|
||||
or
|
||||
exists(ContentSet c | this = TWithoutContentSummaryComponent(c) and result = "without " + c)
|
||||
or
|
||||
exists(ContentSet c | this = TWithContentSummaryComponent(c) and result = "with " + c)
|
||||
or
|
||||
exists(ArgumentPosition pos |
|
||||
this = TParameterSummaryComponent(pos) and result = "parameter " + pos
|
||||
@@ -41,7 +45,13 @@ module Public {
|
||||
/** Provides predicates for constructing summary components. */
|
||||
module SummaryComponent {
|
||||
/** Gets a summary component for content `c`. */
|
||||
SummaryComponent content(Content c) { result = TContentSummaryComponent(c) }
|
||||
SummaryComponent content(ContentSet c) { result = TContentSummaryComponent(c) }
|
||||
|
||||
/** Gets a summary component where data is not allowed to be stored in `c`. */
|
||||
SummaryComponent withoutContent(ContentSet c) { result = TWithoutContentSummaryComponent(c) }
|
||||
|
||||
/** Gets a summary component where data must be stored in `c`. */
|
||||
SummaryComponent withContent(ContentSet c) { result = TWithContentSummaryComponent(c) }
|
||||
|
||||
/** Gets a summary component for a parameter at position `pos`. */
|
||||
SummaryComponent parameter(ArgumentPosition pos) { result = TParameterSummaryComponent(pos) }
|
||||
@@ -185,7 +195,10 @@ module Public {
|
||||
}
|
||||
|
||||
/** A callable with a flow summary. */
|
||||
abstract class SummarizedCallable extends DataFlowCallable {
|
||||
abstract class SummarizedCallable extends SummarizedCallableBase {
|
||||
bindingset[this]
|
||||
SummarizedCallable() { any() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `input` to `output` through this callable.
|
||||
*
|
||||
@@ -216,9 +229,16 @@ module Public {
|
||||
/**
|
||||
* Holds if values stored inside `content` are cleared on objects passed as
|
||||
* arguments at position `pos` to this callable.
|
||||
*
|
||||
* TODO: Remove once all languages support `WithoutContent` tokens.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate clearsContent(ParameterPosition pos, Content content) { none() }
|
||||
predicate clearsContent(ParameterPosition pos, ContentSet content) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the summary is auto generated.
|
||||
*/
|
||||
predicate isAutoGenerated() { none() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,10 +251,12 @@ module Private {
|
||||
import AccessPathSyntax
|
||||
|
||||
newtype TSummaryComponent =
|
||||
TContentSummaryComponent(Content c) or
|
||||
TContentSummaryComponent(ContentSet c) or
|
||||
TParameterSummaryComponent(ArgumentPosition pos) or
|
||||
TArgumentSummaryComponent(ParameterPosition pos) or
|
||||
TReturnSummaryComponent(ReturnKind rk)
|
||||
TReturnSummaryComponent(ReturnKind rk) or
|
||||
TWithoutContentSummaryComponent(ContentSet c) or
|
||||
TWithContentSummaryComponent(ContentSet c)
|
||||
|
||||
private TParameterSummaryComponent thisParam() {
|
||||
result = TParameterSummaryComponent(instanceParameterPosition())
|
||||
@@ -296,6 +318,23 @@ module Private {
|
||||
SummaryComponentStack::singleton(TArgumentSummaryComponent(_))) and
|
||||
preservesValue = preservesValue1.booleanAnd(preservesValue2)
|
||||
)
|
||||
or
|
||||
exists(ParameterPosition ppos, ContentSet cs |
|
||||
c.clearsContent(ppos, cs) and
|
||||
input = SummaryComponentStack::push(SummaryComponent::withoutContent(cs), output) and
|
||||
output = SummaryComponentStack::argument(ppos) and
|
||||
preservesValue = true
|
||||
)
|
||||
}
|
||||
|
||||
private class MkClearStack extends RequiredSummaryComponentStack {
|
||||
override predicate required(SummaryComponent head, SummaryComponentStack tail) {
|
||||
exists(SummarizedCallable sc, ParameterPosition ppos, ContentSet cs |
|
||||
sc.clearsContent(ppos, cs) and
|
||||
head = SummaryComponent::withoutContent(cs) and
|
||||
tail = SummaryComponentStack::argument(ppos)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,10 +417,7 @@ module Private {
|
||||
|
||||
private newtype TSummaryNodeState =
|
||||
TSummaryNodeInputState(SummaryComponentStack s) { inputState(_, s) } or
|
||||
TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) } or
|
||||
TSummaryNodeClearsContentState(ParameterPosition pos, boolean post) {
|
||||
any(SummarizedCallable sc).clearsContent(pos, _) and post in [false, true]
|
||||
}
|
||||
TSummaryNodeOutputState(SummaryComponentStack s) { outputState(_, s) }
|
||||
|
||||
/**
|
||||
* A state used to break up (complex) flow summaries into atomic flow steps.
|
||||
@@ -428,12 +464,6 @@ module Private {
|
||||
this = TSummaryNodeOutputState(s) and
|
||||
result = "to write: " + s
|
||||
)
|
||||
or
|
||||
exists(ParameterPosition pos, boolean post, string postStr |
|
||||
this = TSummaryNodeClearsContentState(pos, post) and
|
||||
(if post = true then postStr = " (post)" else postStr = "") and
|
||||
result = "clear: " + pos + postStr
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,11 +487,6 @@ module Private {
|
||||
not parameterReadState(c, state, _)
|
||||
or
|
||||
state.isOutputState(c, _)
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
c.clearsContent(pos, _) and
|
||||
state = TSummaryNodeClearsContentState(pos, _)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
@@ -471,7 +496,7 @@ module Private {
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
parameterReadState(c, state, pos) and
|
||||
result.(ParamNode).isParameterOf(c, pos)
|
||||
result.(ParamNode).isParameterOf(inject(c), pos)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -497,8 +522,6 @@ module Private {
|
||||
parameterReadState(c, _, pos)
|
||||
or
|
||||
isParameterPostUpdate(_, c, pos)
|
||||
or
|
||||
c.clearsContent(pos, _)
|
||||
}
|
||||
|
||||
private predicate callbackOutput(
|
||||
@@ -506,7 +529,7 @@ module Private {
|
||||
) {
|
||||
any(SummaryNodeState state).isInputState(c, s) and
|
||||
s.head() = TReturnSummaryComponent(rk) and
|
||||
receiver = summaryNodeInputState(c, s.drop(1))
|
||||
receiver = summaryNodeInputState(c, s.tail())
|
||||
}
|
||||
|
||||
private predicate callbackInput(
|
||||
@@ -514,7 +537,7 @@ module Private {
|
||||
) {
|
||||
any(SummaryNodeState state).isOutputState(c, s) and
|
||||
s.head() = TParameterSummaryComponent(pos) and
|
||||
receiver = summaryNodeInputState(c, s.drop(1))
|
||||
receiver = summaryNodeInputState(c, s.tail())
|
||||
}
|
||||
|
||||
/** Holds if a call targeting `receiver` should be synthesized inside `c`. */
|
||||
@@ -540,21 +563,27 @@ module Private {
|
||||
exists(SummarizedCallable c, SummaryComponentStack s, SummaryComponent head | head = s.head() |
|
||||
n = summaryNodeInputState(c, s) and
|
||||
(
|
||||
exists(Content cont |
|
||||
head = TContentSummaryComponent(cont) and result = getContentType(cont)
|
||||
exists(ContentSet cont | result = getContentType(cont) |
|
||||
head = TContentSummaryComponent(cont) or
|
||||
head = TWithContentSummaryComponent(cont)
|
||||
)
|
||||
or
|
||||
exists(ContentSet cont |
|
||||
head = TWithoutContentSummaryComponent(cont) and
|
||||
result = getNodeType(summaryNodeInputState(c, s.tail()))
|
||||
)
|
||||
or
|
||||
exists(ReturnKind rk |
|
||||
head = TReturnSummaryComponent(rk) and
|
||||
result =
|
||||
getCallbackReturnType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c),
|
||||
s.drop(1))), rk)
|
||||
s.tail())), rk)
|
||||
)
|
||||
)
|
||||
or
|
||||
n = summaryNodeOutputState(c, s) and
|
||||
(
|
||||
exists(Content cont |
|
||||
exists(ContentSet cont |
|
||||
head = TContentSummaryComponent(cont) and result = getContentType(cont)
|
||||
)
|
||||
or
|
||||
@@ -567,16 +596,10 @@ module Private {
|
||||
exists(ArgumentPosition pos | head = TParameterSummaryComponent(pos) |
|
||||
result =
|
||||
getCallbackParameterType(getNodeType(summaryNodeInputState(pragma[only_bind_out](c),
|
||||
s.drop(1))), pos)
|
||||
s.tail())), pos)
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(SummarizedCallable c, ParameterPosition pos, ParamNode p |
|
||||
n = summaryNode(c, TSummaryNodeClearsContentState(pos, false)) and
|
||||
p.isParameterOf(c, pos) and
|
||||
result = getNodeType(p)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if summary node `out` contains output of kind `rk` from call `c`. */
|
||||
@@ -601,10 +624,7 @@ module Private {
|
||||
predicate summaryPostUpdateNode(Node post, Node pre) {
|
||||
exists(SummarizedCallable c, ParameterPosition pos |
|
||||
isParameterPostUpdate(post, c, pos) and
|
||||
pre.(ParamNode).isParameterOf(c, pos)
|
||||
or
|
||||
pre = summaryNode(c, TSummaryNodeClearsContentState(pos, false)) and
|
||||
post = summaryNode(c, TSummaryNodeClearsContentState(pos, true))
|
||||
pre.(ParamNode).isParameterOf(inject(c), pos)
|
||||
)
|
||||
or
|
||||
exists(SummarizedCallable callable, SummaryComponentStack s |
|
||||
@@ -627,9 +647,7 @@ module Private {
|
||||
* node, and back out to `p`.
|
||||
*/
|
||||
predicate summaryAllowParameterReturnInSelf(ParamNode p) {
|
||||
exists(SummarizedCallable c, ParameterPosition ppos | p.isParameterOf(c, ppos) |
|
||||
c.clearsContent(ppos, _)
|
||||
or
|
||||
exists(SummarizedCallable c, ParameterPosition ppos | p.isParameterOf(inject(c), ppos) |
|
||||
exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents |
|
||||
summary(c, inputContents, outputContents, _) and
|
||||
inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(ppos)) and
|
||||
@@ -658,9 +676,10 @@ module Private {
|
||||
preservesValue = false and not summary(c, inputContents, outputContents, true)
|
||||
)
|
||||
or
|
||||
exists(SummarizedCallable c, ParameterPosition pos |
|
||||
pred.(ParamNode).isParameterOf(c, pos) and
|
||||
succ = summaryNode(c, TSummaryNodeClearsContentState(pos, _)) and
|
||||
exists(SummarizedCallable c, SummaryComponentStack s |
|
||||
pred = summaryNodeInputState(c, s.tail()) and
|
||||
succ = summaryNodeInputState(c, s) and
|
||||
s.head() = [SummaryComponent::withContent(_), SummaryComponent::withoutContent(_)] and
|
||||
preservesValue = true
|
||||
)
|
||||
}
|
||||
@@ -669,9 +688,9 @@ module Private {
|
||||
* Holds if there is a read step of content `c` from `pred` to `succ`, which
|
||||
* is synthesized from a flow summary.
|
||||
*/
|
||||
predicate summaryReadStep(Node pred, Content c, Node succ) {
|
||||
predicate summaryReadStep(Node pred, ContentSet c, Node succ) {
|
||||
exists(SummarizedCallable sc, SummaryComponentStack s |
|
||||
pred = summaryNodeInputState(sc, s.drop(1)) and
|
||||
pred = summaryNodeInputState(sc, s.tail()) and
|
||||
succ = summaryNodeInputState(sc, s) and
|
||||
SummaryComponent::content(c) = s.head()
|
||||
)
|
||||
@@ -681,10 +700,10 @@ module Private {
|
||||
* Holds if there is a store step of content `c` from `pred` to `succ`, which
|
||||
* is synthesized from a flow summary.
|
||||
*/
|
||||
predicate summaryStoreStep(Node pred, Content c, Node succ) {
|
||||
predicate summaryStoreStep(Node pred, ContentSet c, Node succ) {
|
||||
exists(SummarizedCallable sc, SummaryComponentStack s |
|
||||
pred = summaryNodeOutputState(sc, s) and
|
||||
succ = summaryNodeOutputState(sc, s.drop(1)) and
|
||||
succ = summaryNodeOutputState(sc, s.tail()) and
|
||||
SummaryComponent::content(c) = s.head()
|
||||
)
|
||||
}
|
||||
@@ -708,10 +727,23 @@ module Private {
|
||||
* `a` on line 2 to the post-update node for `a` on that line (via an intermediate
|
||||
* node where field `b` is cleared).
|
||||
*/
|
||||
predicate summaryClearsContent(Node n, Content c) {
|
||||
exists(SummarizedCallable sc, ParameterPosition pos |
|
||||
n = summaryNode(sc, TSummaryNodeClearsContentState(pos, true)) and
|
||||
sc.clearsContent(pos, c)
|
||||
predicate summaryClearsContent(Node n, ContentSet c) {
|
||||
exists(SummarizedCallable sc, SummaryNodeState state, SummaryComponentStack stack |
|
||||
n = summaryNode(sc, state) and
|
||||
state.isInputState(sc, stack) and
|
||||
stack.head() = SummaryComponent::withoutContent(c)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value that is being tracked is expected to be stored inside
|
||||
* content `c` at `n`.
|
||||
*/
|
||||
predicate summaryExpectsContent(Node n, ContentSet c) {
|
||||
exists(SummarizedCallable sc, SummaryNodeState state, SummaryComponentStack stack |
|
||||
n = summaryNode(sc, state) and
|
||||
state.isInputState(sc, stack) and
|
||||
stack.head() = SummaryComponent::withContent(c)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -719,55 +751,79 @@ module Private {
|
||||
private predicate viableParam(
|
||||
DataFlowCall call, SummarizedCallable sc, ParameterPosition ppos, ParamNode p
|
||||
) {
|
||||
p.isParameterOf(sc, ppos) and
|
||||
sc = viableCallable(call)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared inside a
|
||||
* callable to which `arg` is an argument.
|
||||
*
|
||||
* In such cases, it is important to prevent use-use flow out of
|
||||
* `arg` (see comment for `summaryClearsContent`).
|
||||
*/
|
||||
predicate summaryClearsContentArg(ArgNode arg, Content c) {
|
||||
exists(DataFlowCall call, SummarizedCallable sc, ParameterPosition ppos |
|
||||
argumentPositionMatch(call, arg, ppos) and
|
||||
viableParam(call, sc, ppos, _) and
|
||||
sc.clearsContent(ppos, c)
|
||||
exists(DataFlowCallable c |
|
||||
c = inject(sc) and
|
||||
p.isParameterOf(c, ppos) and
|
||||
c = viableCallable(call)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private ParamNode summaryArgParam0(DataFlowCall call, ArgNode arg) {
|
||||
exists(ParameterPosition ppos, SummarizedCallable sc |
|
||||
private ParamNode summaryArgParam0(DataFlowCall call, ArgNode arg, SummarizedCallable sc) {
|
||||
exists(ParameterPosition ppos |
|
||||
argumentPositionMatch(call, arg, ppos) and
|
||||
viableParam(call, sc, ppos, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if use-use flow starting from `arg` should be prohibited.
|
||||
*
|
||||
* This is the case when `arg` is the argument of a call that targets a
|
||||
* flow summary where the corresponding parameter either clears contents
|
||||
* or expects contents.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private ParamNode summaryArgParam(ArgNode arg, ReturnKindExt rk, OutNodeExt out) {
|
||||
exists(DataFlowCall call |
|
||||
result = summaryArgParam0(call, arg) and
|
||||
out = rk.getAnOutNode(call)
|
||||
predicate prohibitsUseUseFlow(ArgNode arg, SummarizedCallable sc) {
|
||||
exists(ParamNode p, Node mid, ParameterPosition ppos, Node ret |
|
||||
p = summaryArgParam0(_, arg, sc) and
|
||||
p.isParameterOf(_, pragma[only_bind_into](ppos)) and
|
||||
summaryLocalStep(p, mid, true) and
|
||||
summaryLocalStep(mid, ret, true) and
|
||||
isParameterPostUpdate(ret, _, pragma[only_bind_into](ppos))
|
||||
|
|
||||
summaryClearsContent(mid, _) or
|
||||
summaryExpectsContent(mid, _)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[ret]
|
||||
private ParamNode summaryArgParam(
|
||||
ArgNode arg, ReturnNodeExt ret, OutNodeExt out, SummarizedCallable sc
|
||||
) {
|
||||
exists(DataFlowCall call, ReturnKindExt rk |
|
||||
result = summaryArgParam0(call, arg, sc) and
|
||||
ret.getKind() = pragma[only_bind_into](rk) and
|
||||
out = pragma[only_bind_into](rk).getAnOutNode(call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` using a simple flow summary, that is, a flow
|
||||
* summary without reads and stores.
|
||||
* Holds if `arg` flows to `out` using a simple value-preserving flow
|
||||
* summary, that is, a flow summary without reads and stores.
|
||||
*
|
||||
* NOTE: This step should not be used in global data-flow/taint-tracking, but may
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryThroughStep(ArgNode arg, Node out, boolean preservesValue) {
|
||||
exists(ReturnKindExt rk, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, rk, out), ret, preservesValue) and
|
||||
ret.getKind() = rk
|
||||
predicate summaryThroughStepValue(ArgNode arg, Node out, SummarizedCallable sc) {
|
||||
exists(ReturnKind rk, ReturnNode ret, DataFlowCall call |
|
||||
summaryLocalStep(summaryArgParam0(call, arg, sc), ret, true) and
|
||||
ret.getKind() = pragma[only_bind_into](rk) and
|
||||
out = getAnOutNode(call, pragma[only_bind_into](rk))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` flows to `out` using a simple flow summary involving taint
|
||||
* step, that is, a flow summary without reads and stores.
|
||||
*
|
||||
* NOTE: This step should not be used in global data-flow/taint-tracking, but may
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryThroughStepTaint(ArgNode arg, Node out, SummarizedCallable sc) {
|
||||
exists(ReturnNodeExt ret | summaryLocalStep(summaryArgParam(arg, ret, out, sc), ret, false))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a read(+taint) of `c` from `arg` to `out` using a
|
||||
* flow summary.
|
||||
@@ -775,11 +831,10 @@ module Private {
|
||||
* NOTE: This step should not be used in global data-flow/taint-tracking, but may
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summaryGetterStep(ArgNode arg, Content c, Node out) {
|
||||
exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret |
|
||||
summaryReadStep(summaryArgParam(arg, rk, out), c, mid) and
|
||||
summaryLocalStep(mid, ret, _) and
|
||||
ret.getKind() = rk
|
||||
predicate summaryGetterStep(ArgNode arg, ContentSet c, Node out, SummarizedCallable sc) {
|
||||
exists(Node mid, ReturnNodeExt ret |
|
||||
summaryReadStep(summaryArgParam(arg, ret, out, sc), c, mid) and
|
||||
summaryLocalStep(mid, ret, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -790,11 +845,10 @@ module Private {
|
||||
* NOTE: This step should not be used in global data-flow/taint-tracking, but may
|
||||
* be useful to include in the exposed local data-flow/taint-tracking relations.
|
||||
*/
|
||||
predicate summarySetterStep(ArgNode arg, Content c, Node out) {
|
||||
exists(ReturnKindExt rk, Node mid, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, rk, out), mid, _) and
|
||||
summaryStoreStep(mid, c, ret) and
|
||||
ret.getKind() = rk
|
||||
predicate summarySetterStep(ArgNode arg, ContentSet c, Node out, SummarizedCallable sc) {
|
||||
exists(Node mid, ReturnNodeExt ret |
|
||||
summaryLocalStep(summaryArgParam(arg, ret, out, sc), mid, _) and
|
||||
summaryStoreStep(mid, c, ret)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -806,10 +860,10 @@ module Private {
|
||||
module External {
|
||||
/** Holds if `spec` is a relevant external specification. */
|
||||
private predicate relevantSpec(string spec) {
|
||||
summaryElement(_, spec, _, _) or
|
||||
summaryElement(_, _, spec, _) or
|
||||
sourceElement(_, spec, _) or
|
||||
sinkElement(_, spec, _)
|
||||
summaryElement(_, spec, _, _, _) or
|
||||
summaryElement(_, _, spec, _, _) or
|
||||
sourceElement(_, spec, _, _) or
|
||||
sinkElement(_, spec, _, _)
|
||||
}
|
||||
|
||||
private class AccessPathRange extends AccessPath::Range {
|
||||
@@ -875,13 +929,27 @@ module Private {
|
||||
}
|
||||
|
||||
private class SummarizedCallableExternal extends SummarizedCallable {
|
||||
SummarizedCallableExternal() { summaryElement(this, _, _, _) }
|
||||
SummarizedCallableExternal() { summaryElement(this, _, _, _, _) }
|
||||
|
||||
private predicate relevantSummaryElementGenerated(
|
||||
AccessPath inSpec, AccessPath outSpec, string kind
|
||||
) {
|
||||
summaryElement(this, inSpec, outSpec, kind, true) and
|
||||
not summaryElement(this, _, _, _, false) and
|
||||
not this.clearsContent(_, _)
|
||||
}
|
||||
|
||||
private predicate relevantSummaryElement(AccessPath inSpec, AccessPath outSpec, string kind) {
|
||||
summaryElement(this, inSpec, outSpec, kind, false)
|
||||
or
|
||||
this.relevantSummaryElementGenerated(inSpec, outSpec, kind)
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(
|
||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
) {
|
||||
exists(AccessPath inSpec, AccessPath outSpec, string kind |
|
||||
summaryElement(this, inSpec, outSpec, kind) and
|
||||
this.relevantSummaryElement(inSpec, outSpec, kind) and
|
||||
interpretSpec(inSpec, input) and
|
||||
interpretSpec(outSpec, output)
|
||||
|
|
||||
@@ -890,6 +958,8 @@ module Private {
|
||||
kind = "taint" and preservesValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAutoGenerated() { this.relevantSummaryElementGenerated(_, _, _) }
|
||||
}
|
||||
|
||||
/** Holds if component `c` of specification `spec` cannot be parsed. */
|
||||
@@ -910,7 +980,7 @@ module Private {
|
||||
|
||||
private predicate sourceElementRef(InterpretNode ref, AccessPath output, string kind) {
|
||||
exists(SourceOrSinkElement e |
|
||||
sourceElement(e, output, kind) and
|
||||
sourceElement(e, output, kind, _) and
|
||||
if outputNeedsReference(output.getToken(0))
|
||||
then e = ref.getCallTarget()
|
||||
else e = ref.asElement()
|
||||
@@ -919,7 +989,7 @@ module Private {
|
||||
|
||||
private predicate sinkElementRef(InterpretNode ref, AccessPath input, string kind) {
|
||||
exists(SourceOrSinkElement e |
|
||||
sinkElement(e, input, kind) and
|
||||
sinkElement(e, input, kind, _) and
|
||||
if inputNeedsReference(input.getToken(0))
|
||||
then e = ref.getCallTarget()
|
||||
else e = ref.asElement()
|
||||
@@ -1025,7 +1095,7 @@ module Private {
|
||||
/** Provides a query predicate for outputting a set of relevant flow summaries. */
|
||||
module TestOutput {
|
||||
/** A flow summary to include in the `summary/3` query predicate. */
|
||||
abstract class RelevantSummarizedCallable extends SummarizedCallable {
|
||||
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
|
||||
/** Gets the string representation of this callable used by `summary/1`. */
|
||||
abstract string getCallableCsv();
|
||||
|
||||
@@ -1033,8 +1103,10 @@ module Private {
|
||||
predicate relevantSummary(
|
||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
) {
|
||||
this.propagatesFlow(input, output, preservesValue)
|
||||
super.propagatesFlow(input, output, preservesValue)
|
||||
}
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
/** Render the kind in the format used in flow summaries. */
|
||||
@@ -1044,9 +1116,13 @@ module Private {
|
||||
preservesValue = false and result = "taint"
|
||||
}
|
||||
|
||||
private string renderProvenance(RelevantSummarizedCallable c) {
|
||||
if c.(SummarizedCallable).isAutoGenerated() then result = "generated" else result = "manual"
|
||||
}
|
||||
|
||||
/**
|
||||
* A query predicate for outputting flow summaries in semi-colon separated format in QL tests.
|
||||
* The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind",
|
||||
* The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind;provenance"",
|
||||
* ext is hardcoded to empty.
|
||||
*/
|
||||
query predicate summary(string csv) {
|
||||
@@ -1056,8 +1132,8 @@ module Private {
|
||||
|
|
||||
c.relevantSummary(input, output, preservesValue) and
|
||||
csv =
|
||||
c.getCallableCsv() + ";;" + getComponentStackCsv(input) + ";" +
|
||||
getComponentStackCsv(output) + ";" + renderKind(preservesValue)
|
||||
c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) +
|
||||
";" + renderKind(preservesValue) + ";" + renderProvenance(c)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1071,19 +1147,21 @@ module Private {
|
||||
*/
|
||||
module RenderSummarizedCallable {
|
||||
/** A summarized callable to include in the graph. */
|
||||
abstract class RelevantSummarizedCallable extends SummarizedCallable { }
|
||||
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
private newtype TNodeOrCall =
|
||||
MkNode(Node n) {
|
||||
exists(RelevantSummarizedCallable c |
|
||||
n = summaryNode(c, _)
|
||||
or
|
||||
n.(ParamNode).isParameterOf(c, _)
|
||||
n.(ParamNode).isParameterOf(inject(c), _)
|
||||
)
|
||||
} or
|
||||
MkCall(DataFlowCall call) {
|
||||
call = summaryDataFlowCall(_) and
|
||||
call.getEnclosingCallable() instanceof RelevantSummarizedCallable
|
||||
call.getEnclosingCallable() = inject(any(RelevantSummarizedCallable c))
|
||||
}
|
||||
|
||||
private class NodeOrCall extends TNodeOrCall {
|
||||
@@ -1123,7 +1201,7 @@ module Private {
|
||||
if preservesValue = true then value = "value" else value = "taint"
|
||||
)
|
||||
or
|
||||
exists(Content c |
|
||||
exists(ContentSet c |
|
||||
Private::Steps::summaryReadStep(a.asNode(), c, b.asNode()) and
|
||||
value = "read (" + c + ")"
|
||||
or
|
||||
@@ -1133,6 +1211,10 @@ module Private {
|
||||
Private::Steps::summaryClearsContent(a.asNode(), c) and
|
||||
b = a and
|
||||
value = "clear (" + c + ")"
|
||||
or
|
||||
Private::Steps::summaryExpectsContent(a.asNode(), c) and
|
||||
b = a and
|
||||
value = "expect (" + c + ")"
|
||||
)
|
||||
or
|
||||
summaryPostUpdateNode(b.asNode(), a.asNode()) and
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
private import go
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowUtil
|
||||
private import FlowSummaryImpl::Private
|
||||
@@ -14,39 +15,12 @@ private module FlowSummaries {
|
||||
private import semmle.go.dataflow.FlowSummary as F
|
||||
}
|
||||
|
||||
/** Holds if `i` is a valid parameter position. */
|
||||
predicate parameterPosition(int i) {
|
||||
i = [-1 .. any(DataFlowCallable c).getType().getNumParameter()]
|
||||
}
|
||||
class SummarizedCallableBase = Callable;
|
||||
|
||||
DataFlowCallable inject(SummarizedCallable c) { result.asCallable() = c }
|
||||
|
||||
/** Gets the parameter position of the instance parameter. */
|
||||
int instanceParameterPosition() { result = -1 }
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition extends int {
|
||||
ParameterPosition() { parameterPosition(this) }
|
||||
}
|
||||
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition extends int {
|
||||
ArgumentPosition() { parameterPosition(this) }
|
||||
}
|
||||
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
|
||||
/**
|
||||
* Holds if `arg` is an argument of `call` with an argument position that matches
|
||||
* parameter position `ppos`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) {
|
||||
exists(ArgumentPosition apos |
|
||||
arg.argumentOf(call, apos) and
|
||||
parameterMatch(ppos, apos)
|
||||
)
|
||||
}
|
||||
ArgumentPosition instanceParameterPosition() { result = -1 }
|
||||
|
||||
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
|
||||
string getParameterPositionCsv(ParameterPosition pos) { result = pos.toString() }
|
||||
@@ -84,13 +58,16 @@ DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { none() }
|
||||
|
||||
/**
|
||||
* Holds if an external flow summary exists for `c` with input specification
|
||||
* `input`, output specification `output`, and kind `kind`.
|
||||
* `input`, output specification `output`, kind `kind`, and a flag `generated`
|
||||
* stating whether the summary is autogenerated.
|
||||
*/
|
||||
predicate summaryElement(DataFlowCallable c, string input, string output, string kind) {
|
||||
predicate summaryElement(
|
||||
SummarizedCallableBase c, string input, string output, string kind, boolean generated
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind) and
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, generated) and
|
||||
c.asFunction() = interpretElement(namespace, type, subtypes, name, signature, ext).asEntity()
|
||||
)
|
||||
}
|
||||
@@ -166,26 +143,28 @@ class SourceOrSinkElement extends TSourceOrSinkElement {
|
||||
|
||||
/**
|
||||
* Holds if an external source specification exists for `e` with output specification
|
||||
* `output` and kind `kind`.
|
||||
* `output`, kind `kind`, and a flag `generated` stating whether the source specification is
|
||||
* autogenerated.
|
||||
*/
|
||||
predicate sourceElement(SourceOrSinkElement e, string output, string kind) {
|
||||
predicate sourceElement(SourceOrSinkElement e, string output, string kind, boolean generated) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind) and
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, generated) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external sink specification exists for `e` with input specification
|
||||
* `input` and kind `kind`.
|
||||
* `input`, kind `kind` and a flag `generated` stating whether the sink specification is
|
||||
* autogenerated.
|
||||
*/
|
||||
predicate sinkElement(SourceOrSinkElement e, string input, string kind) {
|
||||
predicate sinkElement(SourceOrSinkElement e, string input, string kind, boolean generated) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind) and
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, generated) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
@@ -271,7 +250,10 @@ predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if specification component `c` parses as return value `n`. */
|
||||
/**
|
||||
* Holds if specification component `c` parses as return value `n` or a range
|
||||
* containing `n`.
|
||||
*/
|
||||
predicate parseReturn(AccessPathToken c, int n) {
|
||||
(
|
||||
c = "ReturnValue" and n = 0
|
||||
@@ -292,8 +274,10 @@ private int parseConstantOrRange(string arg) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
|
||||
bindingset[arg]
|
||||
ArgumentPosition parseParamBody(string arg) { result = parseConstantOrRange(arg) }
|
||||
|
||||
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
|
||||
bindingset[arg]
|
||||
ParameterPosition parseArgBody(string arg) { result = parseConstantOrRange(arg) }
|
||||
|
||||
@@ -9,12 +9,14 @@ private import FlowSummaryImpl as FlowSummaryImpl
|
||||
* Holds if taint can flow from `src` to `sink` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localTaint(DataFlow::Node src, DataFlow::Node sink) { localTaintStep*(src, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `src` to `sink` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localExprTaint(Expr src, Expr sink) {
|
||||
localTaint(DataFlow::exprNode(src), DataFlow::exprNode(sink))
|
||||
}
|
||||
@@ -27,7 +29,7 @@ predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
localAdditionalTaintStep(src, sink) 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(src, sink, false)
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(src, sink, _)
|
||||
}
|
||||
|
||||
private Type getElementType(Type containerType) {
|
||||
|
||||
@@ -61,15 +61,32 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
@@ -79,6 +96,16 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
@@ -101,8 +128,23 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
* must be taken into account in the analysis.
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
@@ -111,7 +153,25 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
(this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
|
||||
@@ -61,15 +61,32 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
* Holds if `source` is a relevant taint source with the given initial
|
||||
* `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink accepting `state`.
|
||||
*
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
@@ -79,6 +96,16 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `node` is a taint sanitizer when the flow state is
|
||||
* `state`.
|
||||
*/
|
||||
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
|
||||
|
||||
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
this.isSanitizer(node, state)
|
||||
}
|
||||
|
||||
/** Holds if taint propagation into `node` is prohibited. */
|
||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||
|
||||
@@ -101,8 +128,23 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the additional taint propagation step from `node1` to `node2`
|
||||
* must be taken into account in the analysis.
|
||||
* DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
|
||||
*
|
||||
* Holds if taint propagation through nodes guarded by `guard` is prohibited
|
||||
* when the flow state is `state`.
|
||||
*/
|
||||
deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
|
||||
none()
|
||||
}
|
||||
|
||||
deprecated final override predicate isBarrierGuard(
|
||||
DataFlow::BarrierGuard guard, DataFlow::FlowState state
|
||||
) {
|
||||
this.isSanitizerGuard(guard, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
|
||||
|
||||
@@ -111,7 +153,25 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
|
||||
/**
|
||||
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
|
||||
* This step is only applicable in `state1` and updates the flow state to `state2`.
|
||||
*/
|
||||
predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate isAdditionalFlowStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
this.isAdditionalTaintStep(node1, state1, node2, state2)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
(this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
|
||||
@@ -48,11 +48,11 @@ module AllocationSizeOverflow {
|
||||
* Holds if `nd` is at a position where overflow might occur, and its result is used to compute
|
||||
* allocation size `allocsz`.
|
||||
*/
|
||||
predicate isSink(DataFlow::Node nd, DataFlow::Node allocsz) {
|
||||
predicate isSinkWithAllocationSize(DataFlow::Node nd, DataFlow::Node allocsz) {
|
||||
nd.(Sink).getAllocationSize() = allocsz
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node nd) { isSink(nd, _) }
|
||||
override predicate isSink(DataFlow::Node nd) { isSinkWithAllocationSize(nd, _) }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
additionalStep(pred, succ)
|
||||
|
||||
@@ -109,7 +109,7 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
|
||||
* not also in a right-shift expression. We allow this case because it is
|
||||
* a common pattern to serialise `byte(v)`, `byte(v >> 8)`, and so on.
|
||||
*/
|
||||
predicate isSink(DataFlow::TypeCastNode sink, int bitSize) {
|
||||
predicate isSinkWithBitSize(DataFlow::TypeCastNode sink, int bitSize) {
|
||||
sink.asExpr() instanceof ConversionExpr and
|
||||
exists(IntegerType integerType | sink.getResultType().getUnderlyingType() = integerType |
|
||||
bitSize = integerType.getSize()
|
||||
@@ -125,7 +125,7 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSink(sink, sinkBitSize) }
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSinkWithBitSize(sink, sinkBitSize) }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
// To catch flows that only happen on 32-bit architectures we
|
||||
@@ -140,7 +140,7 @@ class ConversionWithoutBoundsCheckConfig extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSanitizerOut(DataFlow::Node node) {
|
||||
exists(int bitSize | isIncorrectIntegerConversion(sourceBitSize, bitSize) |
|
||||
this.isSink(node, bitSize)
|
||||
this.isSinkWithBitSize(node, bitSize)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,10 @@ module InsecureRandomness {
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSink(sink, _) }
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSinkWithKind(sink, _) }
|
||||
|
||||
/** Holds if `sink` is a sink for this configuration with kind `kind`. */
|
||||
predicate isSink(Sink sink, string kind) { kind = sink.getKind() }
|
||||
predicate isSinkWithKind(Sink sink, string kind) { kind = sink.getKind() }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ predicate regexpGuardsError(RegexpPattern regexp) {
|
||||
class Config extends DataFlow::Configuration {
|
||||
Config() { this = "IncompleteHostNameRegexp::Config" }
|
||||
|
||||
predicate isSource(DataFlow::Node source, string hostPart) {
|
||||
predicate isSourceString(DataFlow::Node source, string hostPart) {
|
||||
exists(Expr e |
|
||||
e = source.asExpr() and
|
||||
isIncompleteHostNameRegexpPattern(e.getStringValue(), hostPart)
|
||||
@@ -95,7 +95,7 @@ class Config extends DataFlow::Configuration {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
override predicate isSource(DataFlow::Node source) { isSourceString(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof RegexpPattern and
|
||||
@@ -107,7 +107,7 @@ class Config extends DataFlow::Configuration {
|
||||
}
|
||||
|
||||
from Config c, DataFlow::PathNode source, DataFlow::PathNode sink, string hostPart
|
||||
where c.hasFlowPath(source, sink) and c.isSource(source.getNode(), hostPart)
|
||||
where c.hasFlowPath(source, sink) and c.isSourceString(source.getNode(), hostPart)
|
||||
select source, source, sink,
|
||||
"This regular expression has an unescaped dot before '" + hostPart + "', " +
|
||||
"so it might match more hosts than expected when $@.", sink, "the regular expression is used"
|
||||
|
||||
@@ -63,7 +63,7 @@ predicate isInterestingUnanchoredRegexpString(string re, string msg) {
|
||||
class Config extends DataFlow::Configuration {
|
||||
Config() { this = "MissingRegexpAnchor::Config" }
|
||||
|
||||
predicate isSource(DataFlow::Node source, string msg) {
|
||||
predicate isSourceString(DataFlow::Node source, string msg) {
|
||||
exists(Expr e | e = source.asExpr() |
|
||||
isInterestingUnanchoredRegexpString(e.getStringValue(), msg)
|
||||
or
|
||||
@@ -71,11 +71,11 @@ class Config extends DataFlow::Configuration {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
override predicate isSource(DataFlow::Node source) { isSourceString(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof RegexpPattern }
|
||||
}
|
||||
|
||||
from Config c, DataFlow::PathNode source, string msg
|
||||
where c.hasFlowPath(source, _) and c.isSource(source.getNode(), msg)
|
||||
where c.hasFlowPath(source, _) and c.isSourceString(source.getNode(), msg)
|
||||
select source.getNode(), msg
|
||||
|
||||
@@ -32,7 +32,7 @@ predicate containsEscapedCharacter(DataFlow::Node source, string character) {
|
||||
class Config extends DataFlow::Configuration {
|
||||
Config() { this = "SuspiciousRegexpEscape" }
|
||||
|
||||
predicate isSource(DataFlow::Node source, string report) {
|
||||
predicate isSourceString(DataFlow::Node source, string report) {
|
||||
containsEscapedCharacter(source, "a") and
|
||||
report =
|
||||
"the bell character \\a; did you mean \\\\a, the Vim alphabetic character class (use [[:alpha:]] instead) or \\\\A, the beginning of text?"
|
||||
@@ -41,12 +41,12 @@ class Config extends DataFlow::Configuration {
|
||||
report = "a literal backspace \\b; did you mean \\\\b, a word boundary?"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
override predicate isSource(DataFlow::Node source) { isSourceString(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof RegexpPattern }
|
||||
}
|
||||
|
||||
from Config c, DataFlow::PathNode source, DataFlow::PathNode sink, string report
|
||||
where c.hasFlowPath(source, sink) and c.isSource(source.getNode(), report)
|
||||
where c.hasFlowPath(source, sink) and c.isSourceString(source.getNode(), report)
|
||||
select source, source, sink, "This string literal that is $@ contains " + report, sink,
|
||||
"used as a regular expression"
|
||||
|
||||
@@ -20,7 +20,7 @@ from
|
||||
DataFlow::Node allocsz
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSink(sink.getNode(), allocsz)
|
||||
cfg.isSinkWithAllocationSize(sink.getNode(), allocsz)
|
||||
select sink, source, sink,
|
||||
"This operation, which is used in an $@, involves a $@ and might overflow.", allocsz,
|
||||
"allocation", source, "potentially large value"
|
||||
|
||||
@@ -66,14 +66,14 @@ class HostKeyCallbackAssignmentConfig extends DataFlow::Configuration {
|
||||
/**
|
||||
* Holds if `sink` is a value written by `write` to a field `ClientConfig.HostKeyCallback`.
|
||||
*/
|
||||
predicate isSink(DataFlow::Node sink, Write write) {
|
||||
predicate writeIsSink(DataFlow::Node sink, Write write) {
|
||||
exists(Field f |
|
||||
f.hasQualifiedName(CryptoSsh::packagePath(), "ClientConfig", "HostKeyCallback") and
|
||||
write.writesField(_, f, sink)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSink(sink, _) }
|
||||
override predicate isSink(DataFlow::Node sink) { this.writeIsSink(sink, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,8 +92,8 @@ predicate hostCheckReachesSink(DataFlow::PathNode sink) {
|
||||
SsaWithFields sinkAccessPath, SsaWithFields otherSinkAccessPath
|
||||
|
|
||||
config.hasFlowPath(source, otherSink) and
|
||||
config.isSink(sink.getNode(), sinkWrite) and
|
||||
config.isSink(otherSink.getNode(), otherSinkWrite) and
|
||||
config.writeIsSink(sink.getNode(), sinkWrite) and
|
||||
config.writeIsSink(otherSink.getNode(), otherSinkWrite) and
|
||||
sinkWrite.writesField(sinkAccessPath.getAUse(), _, sink.getNode()) and
|
||||
otherSinkWrite.writesField(otherSinkAccessPath.getAUse(), _, otherSink.getNode()) and
|
||||
otherSinkAccessPath = sinkAccessPath.similar()
|
||||
|
||||
@@ -60,7 +60,7 @@ class TlsVersionFlowConfig extends TaintTracking::Configuration {
|
||||
/**
|
||||
* Holds if `source` is a TLS version source yielding value `val`.
|
||||
*/
|
||||
predicate isSource(DataFlow::Node source, int val) {
|
||||
predicate intIsSource(DataFlow::Node source, int val) {
|
||||
val = source.getIntValue() and
|
||||
val = getATlsVersion() and
|
||||
not DataFlow::isReturnedWithError(source)
|
||||
@@ -74,7 +74,7 @@ class TlsVersionFlowConfig extends TaintTracking::Configuration {
|
||||
fieldWrite.writesField(base, fld, sink)
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
override predicate isSource(DataFlow::Node source) { intIsSource(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { isSink(sink, _, _, _) }
|
||||
}
|
||||
@@ -87,7 +87,7 @@ predicate secureTlsVersionFlow(
|
||||
) {
|
||||
exists(int version |
|
||||
config.hasFlowPath(source, sink) and
|
||||
config.isSource(source.getNode(), version) and
|
||||
config.intIsSource(source.getNode(), version) and
|
||||
not isInsecureTlsVersion(version, _, fld.getName())
|
||||
)
|
||||
}
|
||||
@@ -130,7 +130,7 @@ predicate isInsecureTlsVersionFlow(
|
||||
) {
|
||||
exists(TlsVersionFlowConfig cfg, int version, Field fld |
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSource(source.getNode(), version) and
|
||||
cfg.intIsSource(source.getNode(), version) and
|
||||
cfg.isSink(sink.getNode(), fld, base, _) and
|
||||
isInsecureTlsVersion(version, _, fld.getName()) and
|
||||
// Exclude cases where a secure TLS version can also flow to the same
|
||||
|
||||
@@ -17,7 +17,7 @@ import DataFlow::PathGraph
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, string kind
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSink(sink.getNode(), kind) and
|
||||
cfg.isSinkWithKind(sink.getNode(), kind) and
|
||||
(
|
||||
kind != "A password-related function"
|
||||
or
|
||||
|
||||
@@ -31,7 +31,7 @@ class AuthCodeUrl extends Method {
|
||||
class ConstantStateFlowConf extends DataFlow::Configuration {
|
||||
ConstantStateFlowConf() { this = "ConstantStateFlowConf" }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, DataFlow::CallNode call) {
|
||||
predicate isSinkCall(DataFlow::Node sink, DataFlow::CallNode call) {
|
||||
exists(AuthCodeUrl m | call = m.getACall() | sink = call.getArgument(0))
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class ConstantStateFlowConf extends DataFlow::Configuration {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSink(sink, _) }
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSinkCall(sink, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,11 +109,11 @@ class PrivateUrlFlowsToAuthCodeUrlCall extends DataFlow::Configuration {
|
||||
any(Fmt::AppenderOrSprinter s).taintStep(pred, succ)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, DataFlow::CallNode call) {
|
||||
predicate isSinkCall(DataFlow::Node sink, DataFlow::CallNode call) {
|
||||
exists(AuthCodeUrl m | call = m.getACall() | sink = call.getReceiver())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSink(sink, _) }
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSinkCall(sink, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,7 +126,7 @@ class PrivateUrlFlowsToAuthCodeUrlCall extends DataFlow::Configuration {
|
||||
predicate privateUrlFlowsToAuthCodeUrlCall(DataFlow::CallNode call) {
|
||||
exists(PrivateUrlFlowsToAuthCodeUrlCall flowConfig, DataFlow::Node receiver |
|
||||
flowConfig.hasFlowTo(receiver) and
|
||||
flowConfig.isSink(receiver, call)
|
||||
flowConfig.isSinkCall(receiver, call)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ predicate privateUrlFlowsToAuthCodeUrlCall(DataFlow::CallNode call) {
|
||||
class FlowToPrint extends DataFlow::Configuration {
|
||||
FlowToPrint() { this = "FlowToPrint" }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, DataFlow::CallNode call) {
|
||||
predicate isSinkCall(DataFlow::Node sink, DataFlow::CallNode call) {
|
||||
exists(LoggerCall logCall | call = logCall | sink = logCall.getAMessageComponent())
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ class FlowToPrint extends DataFlow::Configuration {
|
||||
source = any(AuthCodeUrl m).getACall().getResult()
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSink(sink, _) }
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSinkCall(sink, _) }
|
||||
}
|
||||
|
||||
/** Holds if the provided `CallNode`'s result flows to an argument of a printer call. */
|
||||
@@ -198,7 +198,7 @@ from
|
||||
DataFlow::CallNode sinkCall
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSink(sink.getNode(), sinkCall) and
|
||||
cfg.isSinkCall(sink.getNode(), sinkCall) and
|
||||
// Exclude cases that seem to be oauth flows done from within a terminal:
|
||||
not seemsLikeDoneWithinATerminal(sinkCall) and
|
||||
not privateUrlFlowsToAuthCodeUrlCall(sinkCall)
|
||||
|
||||
@@ -94,13 +94,13 @@ predicate urlPath(DataFlow::Node nd) {
|
||||
class Configuration extends TaintTracking::Configuration {
|
||||
Configuration() { this = "BadRedirectCheck" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { this.isSource(source, _) }
|
||||
override predicate isSource(DataFlow::Node source) { this.isCheckedSource(source, _) }
|
||||
|
||||
/**
|
||||
* Holds if `source` is the first node that flows into a use of a variable that is checked by a
|
||||
* bad redirect check `check`..
|
||||
*/
|
||||
predicate isSource(DataFlow::Node source, DataFlow::Node check) {
|
||||
predicate isCheckedSource(DataFlow::Node source, DataFlow::Node check) {
|
||||
exists(SsaWithFields v |
|
||||
DataFlow::localFlow(source, v.getAUse()) and
|
||||
not exists(source.getAPredecessor()) and
|
||||
@@ -170,7 +170,7 @@ predicate isBadRedirectCheckWrapper(DataFlow::Node check, FuncDef f, FunctionInp
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, DataFlow::Node check
|
||||
where
|
||||
cfg.isSource(source.getNode(), check) and
|
||||
cfg.isCheckedSource(source.getNode(), check) and
|
||||
cfg.hasFlowPath(source, sink)
|
||||
select check, source, sink,
|
||||
"This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position.",
|
||||
|
||||
@@ -61,7 +61,7 @@ class FlowsUntrustedToAllowOriginHeader extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, AllowOriginHeaderWrite hw) { sink = hw.getValue() }
|
||||
predicate isSinkHW(DataFlow::Node sink, AllowOriginHeaderWrite hw) { sink = hw.getValue() }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
exists(ControlFlow::ConditionGuardNode cgn |
|
||||
@@ -71,7 +71,7 @@ class FlowsUntrustedToAllowOriginHeader extends TaintTracking::Configuration {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSink(sink, _) }
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSinkHW(sink, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +95,7 @@ predicate allowCredentialsIsSetToTrue(AllowOriginHeaderWrite allowOriginHW) {
|
||||
predicate flowsFromUntrustedToAllowOrigin(AllowOriginHeaderWrite allowOriginHW, string message) {
|
||||
exists(FlowsUntrustedToAllowOriginHeader cfg, DataFlow::Node sink |
|
||||
cfg.hasFlowTo(sink) and
|
||||
cfg.isSink(sink, allowOriginHW)
|
||||
cfg.isSinkHW(sink, allowOriginHW)
|
||||
|
|
||||
message =
|
||||
headerAllowOrigin() + " header is set to a user-defined value, and " +
|
||||
@@ -130,9 +130,9 @@ class FlowsFromUntrusted extends TaintTracking::Configuration {
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSink(sink, _) }
|
||||
override predicate isSink(DataFlow::Node sink) { this.isSinkCgn(sink, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, ControlFlow::ConditionGuardNode cgn) {
|
||||
predicate isSinkCgn(DataFlow::Node sink, ControlFlow::ConditionGuardNode cgn) {
|
||||
exists(IfStmt ifs |
|
||||
exists(Expr operand |
|
||||
operand = ifs.getCond().getAChildExpr*() and
|
||||
@@ -171,7 +171,7 @@ class FlowsFromUntrusted extends TaintTracking::Configuration {
|
||||
*/
|
||||
predicate flowsToGuardedByCheckOnUntrusted(AllowOriginHeaderWrite allowOriginHW) {
|
||||
exists(FlowsFromUntrusted cfg, DataFlow::Node sink, ControlFlow::ConditionGuardNode cgn |
|
||||
cfg.hasFlowTo(sink) and cfg.isSink(sink, cgn)
|
||||
cfg.hasFlowTo(sink) and cfg.isSinkCgn(sink, cgn)
|
||||
|
|
||||
cgn.dominates(allowOriginHW.getBasicBlock())
|
||||
)
|
||||
|
||||
@@ -41,16 +41,18 @@ class ConversionToUnsafePointer extends DataFlow::TypeCastNode {
|
||||
class UnsafeTypeCastingConf extends TaintTracking::Configuration {
|
||||
UnsafeTypeCastingConf() { this = "UnsafeTypeCastingConf" }
|
||||
|
||||
predicate isSource(DataFlow::Node source, ConversionToUnsafePointer conv) { source = conv }
|
||||
predicate conversionIsSource(DataFlow::Node source, ConversionToUnsafePointer conv) {
|
||||
source = conv
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, DataFlow::TypeCastNode ca) {
|
||||
predicate typeCastNodeIsSink(DataFlow::Node sink, DataFlow::TypeCastNode ca) {
|
||||
ca.getOperand().getType() instanceof UnsafePointerType and
|
||||
sink = ca
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
override predicate isSource(DataFlow::Node source) { conversionIsSource(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
|
||||
override predicate isSink(DataFlow::Node sink) { typeCastNodeIsSink(sink, _) }
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -66,8 +68,8 @@ predicate castShortArrayToLongerArray(
|
||||
ArrayType arrTo, ArrayType arrFrom, int arrFromSize
|
||||
|
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSource(source.getNode(), castLittle) and
|
||||
cfg.isSink(sink.getNode(), castBig) and
|
||||
cfg.conversionIsSource(source.getNode(), castLittle) and
|
||||
cfg.typeCastNodeIsSink(sink.getNode(), castBig) and
|
||||
arrTo = getFinalType(castBig.getResultType()) and
|
||||
(
|
||||
// Array (whole) to array:
|
||||
@@ -111,8 +113,8 @@ predicate castTypeToArray(DataFlow::PathNode source, DataFlow::PathNode sink, st
|
||||
ArrayType arrTo, Type typeFrom
|
||||
|
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSource(source.getNode(), castLittle) and
|
||||
cfg.isSink(sink.getNode(), castBig) and
|
||||
cfg.conversionIsSource(source.getNode(), castLittle) and
|
||||
cfg.typeCastNodeIsSink(sink.getNode(), castBig) and
|
||||
arrTo = getFinalType(castBig.getResultType()) and
|
||||
not typeFrom.getUnderlyingType() instanceof ArrayType and
|
||||
not typeFrom instanceof PointerType and
|
||||
@@ -141,8 +143,8 @@ predicate castDifferentBitSizeNumbers(
|
||||
NumericType numTo, NumericType numFrom
|
||||
|
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSource(source.getNode(), castLittle) and
|
||||
cfg.isSink(sink.getNode(), castBig) and
|
||||
cfg.conversionIsSource(source.getNode(), castLittle) and
|
||||
cfg.typeCastNodeIsSink(sink.getNode(), castBig) and
|
||||
numTo = getFinalType(castBig.getResultType()) and
|
||||
numFrom = getFinalType(castLittle.getOperand().getType()) and
|
||||
// TODO: also consider cast from uint to int?
|
||||
|
||||
@@ -13,11 +13,6 @@ edges
|
||||
| new-tests.go:62:31:62:38 | selection of Body : ReadCloser | new-tests.go:69:11:69:57 | call to Sprintf |
|
||||
| new-tests.go:62:31:62:38 | selection of Body : ReadCloser | new-tests.go:74:12:74:58 | call to Sprintf |
|
||||
| new-tests.go:78:18:78:24 | selection of URL : pointer type | new-tests.go:79:11:79:46 | ...+... |
|
||||
| new-tests.go:81:37:81:43 | implicit dereference : URL | new-tests.go:81:37:81:43 | implicit dereference : URL |
|
||||
| new-tests.go:81:37:81:43 | implicit dereference : URL | new-tests.go:81:37:81:43 | selection of URL : pointer type |
|
||||
| new-tests.go:81:37:81:43 | implicit dereference : URL | new-tests.go:82:11:82:46 | ...+... |
|
||||
| new-tests.go:81:37:81:43 | selection of URL : pointer type | new-tests.go:81:37:81:43 | implicit dereference : URL |
|
||||
| new-tests.go:81:37:81:43 | selection of URL : pointer type | new-tests.go:81:37:81:43 | selection of URL : pointer type |
|
||||
| new-tests.go:81:37:81:43 | selection of URL : pointer type | new-tests.go:82:11:82:46 | ...+... |
|
||||
| new-tests.go:86:10:86:20 | call to Vars : map type | new-tests.go:88:11:88:46 | ...+... |
|
||||
| new-tests.go:95:18:95:45 | call to URLParam : string | new-tests.go:96:11:96:46 | ...+... |
|
||||
@@ -46,7 +41,6 @@ nodes
|
||||
| new-tests.go:74:12:74:58 | call to Sprintf | semmle.label | call to Sprintf |
|
||||
| new-tests.go:78:18:78:24 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
|
||||
| new-tests.go:79:11:79:46 | ...+... | semmle.label | ...+... |
|
||||
| new-tests.go:81:37:81:43 | implicit dereference : URL | semmle.label | implicit dereference : URL |
|
||||
| new-tests.go:81:37:81:43 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
|
||||
| new-tests.go:82:11:82:46 | ...+... | semmle.label | ...+... |
|
||||
| new-tests.go:86:10:86:20 | call to Vars : map type | semmle.label | call to Vars : map type |
|
||||
|
||||
@@ -22,5 +22,5 @@ class SummaryModelTest extends SummaryModelCsv {
|
||||
}
|
||||
|
||||
from DataFlow::Node node1, DataFlow::Node node2
|
||||
where FlowSummaryImpl::Private::Steps::summaryThroughStep(node1, node2, false)
|
||||
where FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(node1, node2, _)
|
||||
select node1, node2
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
edges
|
||||
| test.go:27:6:27:10 | definition of bound : bindMe | test.go:29:13:29:30 | type conversion |
|
||||
| test.go:27:6:27:10 | definition of bound : bindMe | test.go:29:20:29:26 | selection of a : slice type |
|
||||
| test.go:27:6:27:10 | definition of bound : bindMe | test.go:30:13:30:27 | type conversion |
|
||||
| test.go:27:6:27:10 | definition of bound : bindMe | test.go:31:13:31:29 | type conversion |
|
||||
| test.go:27:6:27:10 | definition of bound : bindMe | test.go:31:20:31:26 | selection of c : subBindMe |
|
||||
| test.go:29:20:29:26 | selection of a : slice type | test.go:29:13:29:30 | type conversion |
|
||||
| test.go:31:20:31:26 | selection of c : subBindMe | test.go:31:13:31:29 | type conversion |
|
||||
| test.go:36:20:36:42 | call to Cookie : string | test.go:36:13:36:43 | type conversion |
|
||||
| test.go:41:20:41:31 | call to Data : map type | test.go:41:13:41:52 | type conversion |
|
||||
| test.go:46:20:46:43 | call to GetData : basic interface type | test.go:46:13:46:53 | type conversion |
|
||||
@@ -30,18 +26,7 @@ edges
|
||||
| test.go:202:18:202:33 | selection of Form : Values | test.go:203:14:203:28 | type conversion |
|
||||
| test.go:217:2:217:34 | ... := ...[0] : File | test.go:220:14:220:20 | content |
|
||||
| test.go:217:2:217:34 | ... := ...[1] : pointer type | test.go:218:14:218:32 | type conversion |
|
||||
| test.go:217:2:217:34 | ... := ...[1] : pointer type | test.go:218:21:218:22 | implicit dereference : FileHeader |
|
||||
| test.go:218:21:218:22 | implicit dereference : FileHeader | test.go:218:14:218:32 | type conversion |
|
||||
| test.go:218:21:218:22 | implicit dereference : FileHeader | test.go:218:21:218:22 | implicit dereference : FileHeader |
|
||||
| test.go:222:2:222:40 | ... := ...[0] : slice type | test.go:223:14:223:38 | type conversion |
|
||||
| test.go:222:2:222:40 | ... := ...[0] : slice type | test.go:223:21:223:28 | implicit dereference : FileHeader |
|
||||
| test.go:222:2:222:40 | ... := ...[0] : slice type | test.go:223:21:223:28 | index expression : pointer type |
|
||||
| test.go:223:21:223:28 | implicit dereference : FileHeader | test.go:223:14:223:38 | type conversion |
|
||||
| test.go:223:21:223:28 | implicit dereference : FileHeader | test.go:223:21:223:28 | implicit dereference : FileHeader |
|
||||
| test.go:223:21:223:28 | implicit dereference : FileHeader | test.go:223:21:223:28 | index expression : pointer type |
|
||||
| test.go:223:21:223:28 | index expression : pointer type | test.go:223:14:223:38 | type conversion |
|
||||
| test.go:223:21:223:28 | index expression : pointer type | test.go:223:21:223:28 | implicit dereference : FileHeader |
|
||||
| test.go:223:21:223:28 | index expression : pointer type | test.go:223:21:223:28 | index expression : pointer type |
|
||||
| test.go:225:7:225:28 | call to GetString : string | test.go:226:14:226:22 | type conversion |
|
||||
| test.go:228:8:228:35 | call to GetStrings : slice type | test.go:229:14:229:26 | type conversion |
|
||||
| test.go:231:9:231:17 | call to Input : Values | test.go:232:14:232:27 | type conversion |
|
||||
@@ -50,104 +35,26 @@ edges
|
||||
| test.go:253:23:253:44 | call to GetCookie : string | test.go:253:16:253:45 | type conversion |
|
||||
| test.go:264:62:264:83 | call to GetCookie : string | test.go:264:55:264:84 | type conversion |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:277:21:277:61 | call to GetDisplayString |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:277:44:277:51 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:277:44:277:51 | index expression : pointer type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:278:21:278:53 | call to SliceChunk : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:278:21:278:56 | index expression : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:278:21:278:83 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:278:21:278:92 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:279:21:279:60 | call to SliceDiff : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:279:21:279:87 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:279:21:279:96 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:284:3:286:44 | call to SliceFilter : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:284:3:286:71 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:284:3:286:80 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:287:21:287:65 | call to SliceIntersect : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:287:21:287:92 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:287:21:287:101 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:288:21:288:65 | call to SliceIntersect : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:288:21:288:92 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:288:21:288:101 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:289:21:289:61 | call to SliceMerge : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:289:21:289:88 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:289:21:289:97 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:290:21:290:61 | call to SliceMerge : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:290:21:290:88 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:290:21:290:97 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:291:21:291:66 | call to SlicePad : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:291:21:291:93 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:291:21:291:102 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:292:21:292:66 | call to SlicePad : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:292:21:292:93 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:292:21:292:102 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:293:21:293:73 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:293:21:293:82 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:295:21:295:97 | call to SliceReduce : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:295:21:295:124 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:295:21:295:133 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:296:21:296:52 | call to SliceShuffle : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:296:21:296:79 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:296:21:296:88 | selection of Filename |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:297:21:297:51 | call to SliceUnique : slice type |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:297:21:297:78 | implicit dereference : FileHeader |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | test.go:297:21:297:87 | selection of Filename |
|
||||
| test.go:277:44:277:51 | implicit dereference : FileHeader | test.go:277:21:277:61 | call to GetDisplayString |
|
||||
| test.go:277:44:277:51 | implicit dereference : FileHeader | test.go:277:44:277:51 | implicit dereference : FileHeader |
|
||||
| test.go:277:44:277:51 | implicit dereference : FileHeader | test.go:277:44:277:51 | index expression : pointer type |
|
||||
| test.go:277:44:277:51 | index expression : pointer type | test.go:277:21:277:61 | call to GetDisplayString |
|
||||
| test.go:277:44:277:51 | index expression : pointer type | test.go:277:44:277:51 | implicit dereference : FileHeader |
|
||||
| test.go:277:44:277:51 | index expression : pointer type | test.go:277:44:277:51 | index expression : pointer type |
|
||||
| test.go:278:21:278:53 | call to SliceChunk : slice type | test.go:278:21:278:56 | index expression : slice type |
|
||||
| test.go:278:21:278:53 | call to SliceChunk : slice type | test.go:278:21:278:83 | implicit dereference : FileHeader |
|
||||
| test.go:278:21:278:53 | call to SliceChunk : slice type | test.go:278:21:278:92 | selection of Filename |
|
||||
| test.go:278:21:278:56 | index expression : slice type | test.go:278:21:278:83 | implicit dereference : FileHeader |
|
||||
| test.go:278:21:278:56 | index expression : slice type | test.go:278:21:278:92 | selection of Filename |
|
||||
| test.go:278:21:278:83 | implicit dereference : FileHeader | test.go:278:21:278:92 | selection of Filename |
|
||||
| test.go:279:21:279:60 | call to SliceDiff : slice type | test.go:279:21:279:87 | implicit dereference : FileHeader |
|
||||
| test.go:279:21:279:60 | call to SliceDiff : slice type | test.go:279:21:279:96 | selection of Filename |
|
||||
| test.go:279:21:279:87 | implicit dereference : FileHeader | test.go:279:21:279:96 | selection of Filename |
|
||||
| test.go:284:3:286:44 | call to SliceFilter : slice type | test.go:284:3:286:71 | implicit dereference : FileHeader |
|
||||
| test.go:284:3:286:44 | call to SliceFilter : slice type | test.go:284:3:286:80 | selection of Filename |
|
||||
| test.go:284:3:286:71 | implicit dereference : FileHeader | test.go:284:3:286:80 | selection of Filename |
|
||||
| test.go:287:21:287:65 | call to SliceIntersect : slice type | test.go:287:21:287:92 | implicit dereference : FileHeader |
|
||||
| test.go:287:21:287:65 | call to SliceIntersect : slice type | test.go:287:21:287:101 | selection of Filename |
|
||||
| test.go:287:21:287:92 | implicit dereference : FileHeader | test.go:287:21:287:101 | selection of Filename |
|
||||
| test.go:288:21:288:65 | call to SliceIntersect : slice type | test.go:288:21:288:92 | implicit dereference : FileHeader |
|
||||
| test.go:288:21:288:65 | call to SliceIntersect : slice type | test.go:288:21:288:101 | selection of Filename |
|
||||
| test.go:288:21:288:92 | implicit dereference : FileHeader | test.go:288:21:288:101 | selection of Filename |
|
||||
| test.go:289:21:289:61 | call to SliceMerge : slice type | test.go:289:21:289:88 | implicit dereference : FileHeader |
|
||||
| test.go:289:21:289:61 | call to SliceMerge : slice type | test.go:289:21:289:97 | selection of Filename |
|
||||
| test.go:289:21:289:88 | implicit dereference : FileHeader | test.go:289:21:289:97 | selection of Filename |
|
||||
| test.go:290:21:290:61 | call to SliceMerge : slice type | test.go:290:21:290:88 | implicit dereference : FileHeader |
|
||||
| test.go:290:21:290:61 | call to SliceMerge : slice type | test.go:290:21:290:97 | selection of Filename |
|
||||
| test.go:290:21:290:88 | implicit dereference : FileHeader | test.go:290:21:290:97 | selection of Filename |
|
||||
| test.go:291:21:291:66 | call to SlicePad : slice type | test.go:291:21:291:93 | implicit dereference : FileHeader |
|
||||
| test.go:291:21:291:66 | call to SlicePad : slice type | test.go:291:21:291:102 | selection of Filename |
|
||||
| test.go:291:21:291:93 | implicit dereference : FileHeader | test.go:291:21:291:102 | selection of Filename |
|
||||
| test.go:292:21:292:66 | call to SlicePad : slice type | test.go:292:21:292:93 | implicit dereference : FileHeader |
|
||||
| test.go:292:21:292:66 | call to SlicePad : slice type | test.go:292:21:292:102 | selection of Filename |
|
||||
| test.go:292:21:292:93 | implicit dereference : FileHeader | test.go:292:21:292:102 | selection of Filename |
|
||||
| test.go:293:21:293:73 | implicit dereference : FileHeader | test.go:293:21:293:82 | selection of Filename |
|
||||
| test.go:295:21:295:97 | call to SliceReduce : slice type | test.go:295:21:295:124 | implicit dereference : FileHeader |
|
||||
| test.go:295:21:295:97 | call to SliceReduce : slice type | test.go:295:21:295:133 | selection of Filename |
|
||||
| test.go:295:21:295:124 | implicit dereference : FileHeader | test.go:295:21:295:133 | selection of Filename |
|
||||
| test.go:296:21:296:52 | call to SliceShuffle : slice type | test.go:296:21:296:79 | implicit dereference : FileHeader |
|
||||
| test.go:296:21:296:52 | call to SliceShuffle : slice type | test.go:296:21:296:88 | selection of Filename |
|
||||
| test.go:296:21:296:79 | implicit dereference : FileHeader | test.go:296:21:296:88 | selection of Filename |
|
||||
| test.go:297:21:297:51 | call to SliceUnique : slice type | test.go:297:21:297:78 | implicit dereference : FileHeader |
|
||||
| test.go:297:21:297:51 | call to SliceUnique : slice type | test.go:297:21:297:87 | selection of Filename |
|
||||
| test.go:297:21:297:78 | implicit dereference : FileHeader | test.go:297:21:297:87 | selection of Filename |
|
||||
| test.go:303:15:303:36 | call to GetString : string | test.go:305:21:305:48 | type assertion |
|
||||
| test.go:303:15:303:36 | call to GetString : string | test.go:306:21:306:32 | call to Items : map type |
|
||||
| test.go:303:15:303:36 | call to GetString : string | test.go:306:21:306:52 | type assertion |
|
||||
| test.go:306:21:306:32 | call to Items : map type | test.go:306:21:306:52 | type assertion |
|
||||
nodes
|
||||
| test.go:27:6:27:10 | definition of bound : bindMe | semmle.label | definition of bound : bindMe |
|
||||
| test.go:29:13:29:30 | type conversion | semmle.label | type conversion |
|
||||
| test.go:29:20:29:26 | selection of a : slice type | semmle.label | selection of a : slice type |
|
||||
| test.go:30:13:30:27 | type conversion | semmle.label | type conversion |
|
||||
| test.go:31:13:31:29 | type conversion | semmle.label | type conversion |
|
||||
| test.go:31:20:31:26 | selection of c : subBindMe | semmle.label | selection of c : subBindMe |
|
||||
| test.go:36:13:36:43 | type conversion | semmle.label | type conversion |
|
||||
| test.go:36:20:36:42 | call to Cookie : string | semmle.label | call to Cookie : string |
|
||||
| test.go:41:13:41:52 | type conversion | semmle.label | type conversion |
|
||||
@@ -191,12 +98,9 @@ nodes
|
||||
| test.go:217:2:217:34 | ... := ...[0] : File | semmle.label | ... := ...[0] : File |
|
||||
| test.go:217:2:217:34 | ... := ...[1] : pointer type | semmle.label | ... := ...[1] : pointer type |
|
||||
| test.go:218:14:218:32 | type conversion | semmle.label | type conversion |
|
||||
| test.go:218:21:218:22 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:220:14:220:20 | content | semmle.label | content |
|
||||
| test.go:222:2:222:40 | ... := ...[0] : slice type | semmle.label | ... := ...[0] : slice type |
|
||||
| test.go:223:14:223:38 | type conversion | semmle.label | type conversion |
|
||||
| test.go:223:21:223:28 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:223:21:223:28 | index expression : pointer type | semmle.label | index expression : pointer type |
|
||||
| test.go:225:7:225:28 | call to GetString : string | semmle.label | call to GetString : string |
|
||||
| test.go:226:14:226:22 | type conversion | semmle.label | type conversion |
|
||||
| test.go:228:8:228:35 | call to GetStrings : slice type | semmle.label | call to GetStrings : slice type |
|
||||
@@ -215,50 +119,21 @@ nodes
|
||||
| test.go:264:62:264:83 | call to GetCookie : string | semmle.label | call to GetCookie : string |
|
||||
| test.go:269:2:269:40 | ... := ...[0] : slice type | semmle.label | ... := ...[0] : slice type |
|
||||
| test.go:277:21:277:61 | call to GetDisplayString | semmle.label | call to GetDisplayString |
|
||||
| test.go:277:44:277:51 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:277:44:277:51 | index expression : pointer type | semmle.label | index expression : pointer type |
|
||||
| test.go:278:21:278:53 | call to SliceChunk : slice type | semmle.label | call to SliceChunk : slice type |
|
||||
| test.go:278:21:278:56 | index expression : slice type | semmle.label | index expression : slice type |
|
||||
| test.go:278:21:278:83 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:278:21:278:92 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:279:21:279:60 | call to SliceDiff : slice type | semmle.label | call to SliceDiff : slice type |
|
||||
| test.go:279:21:279:87 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:279:21:279:96 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:284:3:286:44 | call to SliceFilter : slice type | semmle.label | call to SliceFilter : slice type |
|
||||
| test.go:284:3:286:71 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:284:3:286:80 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:287:21:287:65 | call to SliceIntersect : slice type | semmle.label | call to SliceIntersect : slice type |
|
||||
| test.go:287:21:287:92 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:287:21:287:101 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:288:21:288:65 | call to SliceIntersect : slice type | semmle.label | call to SliceIntersect : slice type |
|
||||
| test.go:288:21:288:92 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:288:21:288:101 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:289:21:289:61 | call to SliceMerge : slice type | semmle.label | call to SliceMerge : slice type |
|
||||
| test.go:289:21:289:88 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:289:21:289:97 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:290:21:290:61 | call to SliceMerge : slice type | semmle.label | call to SliceMerge : slice type |
|
||||
| test.go:290:21:290:88 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:290:21:290:97 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:291:21:291:66 | call to SlicePad : slice type | semmle.label | call to SlicePad : slice type |
|
||||
| test.go:291:21:291:93 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:291:21:291:102 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:292:21:292:66 | call to SlicePad : slice type | semmle.label | call to SlicePad : slice type |
|
||||
| test.go:292:21:292:93 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:292:21:292:102 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:293:21:293:73 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:293:21:293:82 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:295:21:295:97 | call to SliceReduce : slice type | semmle.label | call to SliceReduce : slice type |
|
||||
| test.go:295:21:295:124 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:295:21:295:133 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:296:21:296:52 | call to SliceShuffle : slice type | semmle.label | call to SliceShuffle : slice type |
|
||||
| test.go:296:21:296:79 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:296:21:296:88 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:297:21:297:51 | call to SliceUnique : slice type | semmle.label | call to SliceUnique : slice type |
|
||||
| test.go:297:21:297:78 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| test.go:297:21:297:87 | selection of Filename | semmle.label | selection of Filename |
|
||||
| test.go:303:15:303:36 | call to GetString : string | semmle.label | call to GetString : string |
|
||||
| test.go:305:21:305:48 | type assertion | semmle.label | type assertion |
|
||||
| test.go:306:21:306:32 | call to Items : map type | semmle.label | call to Items : map type |
|
||||
| test.go:306:21:306:52 | type assertion | semmle.label | type assertion |
|
||||
subpaths
|
||||
#select
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
edges
|
||||
| test.go:77:13:77:16 | &... : pointer type | test.go:78:13:78:29 | type conversion |
|
||||
| test.go:77:13:77:16 | &... : pointer type | test.go:79:13:79:43 | type conversion |
|
||||
| test.go:77:13:77:16 | &... : pointer type | test.go:79:20:79:33 | selection of substructs : slice type |
|
||||
| test.go:77:13:77:16 | &... : pointer type | test.go:79:20:79:36 | index expression : SubStruct |
|
||||
| test.go:79:20:79:33 | selection of substructs : slice type | test.go:79:13:79:43 | type conversion |
|
||||
| test.go:79:20:79:33 | selection of substructs : slice type | test.go:79:20:79:36 | index expression : SubStruct |
|
||||
| test.go:79:20:79:36 | index expression : SubStruct | test.go:79:13:79:43 | type conversion |
|
||||
| test.go:82:22:82:26 | &... : pointer type | test.go:83:13:83:30 | type conversion |
|
||||
| test.go:86:21:86:25 | &... : pointer type | test.go:87:13:87:30 | type conversion |
|
||||
| test.go:92:20:92:36 | call to Value : string | test.go:92:13:92:37 | type conversion |
|
||||
@@ -18,14 +13,6 @@ edges
|
||||
| test.go:99:20:99:40 | call to RawValue : basic interface type | test.go:99:13:99:50 | type conversion |
|
||||
| test.go:100:20:100:38 | call to String : string | test.go:100:13:100:39 | type conversion |
|
||||
| test.go:106:9:106:13 | &... : pointer type | test.go:107:13:107:33 | type conversion |
|
||||
| test.go:106:9:106:13 | &... : pointer type | test.go:107:20:107:26 | implicit dereference : MyStruct |
|
||||
| test.go:106:9:106:13 | &... : pointer type | test.go:107:20:107:26 | index expression : pointer type |
|
||||
| test.go:107:20:107:26 | implicit dereference : MyStruct | test.go:107:13:107:33 | type conversion |
|
||||
| test.go:107:20:107:26 | implicit dereference : MyStruct | test.go:107:20:107:26 | implicit dereference : MyStruct |
|
||||
| test.go:107:20:107:26 | implicit dereference : MyStruct | test.go:107:20:107:26 | index expression : pointer type |
|
||||
| test.go:107:20:107:26 | index expression : pointer type | test.go:107:13:107:33 | type conversion |
|
||||
| test.go:107:20:107:26 | index expression : pointer type | test.go:107:20:107:26 | implicit dereference : MyStruct |
|
||||
| test.go:107:20:107:26 | index expression : pointer type | test.go:107:20:107:26 | index expression : pointer type |
|
||||
| test.go:110:9:110:12 | &... : pointer type | test.go:111:13:111:29 | type conversion |
|
||||
| test.go:114:12:114:19 | &... : pointer type | test.go:115:13:115:48 | type conversion |
|
||||
| test.go:118:16:118:24 | &... : pointer type | test.go:119:13:119:43 | type conversion |
|
||||
@@ -43,8 +30,6 @@ nodes
|
||||
| test.go:77:13:77:16 | &... : pointer type | semmle.label | &... : pointer type |
|
||||
| test.go:78:13:78:29 | type conversion | semmle.label | type conversion |
|
||||
| test.go:79:13:79:43 | type conversion | semmle.label | type conversion |
|
||||
| test.go:79:20:79:33 | selection of substructs : slice type | semmle.label | selection of substructs : slice type |
|
||||
| test.go:79:20:79:36 | index expression : SubStruct | semmle.label | index expression : SubStruct |
|
||||
| test.go:82:22:82:26 | &... : pointer type | semmle.label | &... : pointer type |
|
||||
| test.go:83:13:83:30 | type conversion | semmle.label | type conversion |
|
||||
| test.go:86:21:86:25 | &... : pointer type | semmle.label | &... : pointer type |
|
||||
@@ -69,8 +54,6 @@ nodes
|
||||
| test.go:100:20:100:38 | call to String : string | semmle.label | call to String : string |
|
||||
| test.go:106:9:106:13 | &... : pointer type | semmle.label | &... : pointer type |
|
||||
| test.go:107:13:107:33 | type conversion | semmle.label | type conversion |
|
||||
| test.go:107:20:107:26 | implicit dereference : MyStruct | semmle.label | implicit dereference : MyStruct |
|
||||
| test.go:107:20:107:26 | index expression : pointer type | semmle.label | index expression : pointer type |
|
||||
| test.go:110:9:110:12 | &... : pointer type | semmle.label | &... : pointer type |
|
||||
| test.go:111:13:111:29 | type conversion | semmle.label | type conversion |
|
||||
| test.go:114:12:114:19 | &... : pointer type | semmle.label | &... : pointer type |
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
edges
|
||||
| test.go:13:12:13:16 | implicit dereference : URL | test.go:13:12:13:16 | implicit dereference : URL |
|
||||
| test.go:13:12:13:16 | implicit dereference : URL | test.go:13:12:13:16 | selection of URL : pointer type |
|
||||
| test.go:13:12:13:16 | implicit dereference : URL | test.go:13:12:13:21 | selection of Path : string |
|
||||
| test.go:13:12:13:16 | selection of URL : pointer type | test.go:13:12:13:16 | implicit dereference : URL |
|
||||
| test.go:13:12:13:16 | selection of URL : pointer type | test.go:13:12:13:16 | selection of URL : pointer type |
|
||||
| test.go:13:12:13:16 | selection of URL : pointer type | test.go:13:12:13:21 | selection of Path : string |
|
||||
| test.go:13:12:13:21 | selection of Path : string | test.go:21:18:21:23 | hidden : string |
|
||||
| test.go:21:18:21:23 | hidden : string | test.go:21:11:21:24 | type conversion |
|
||||
@@ -11,7 +6,6 @@ edges
|
||||
| test.go:23:18:23:60 | call to URLParamFromCtx : string | test.go:23:11:23:61 | type conversion |
|
||||
| test.go:24:18:24:71 | call to URLParam : string | test.go:24:11:24:72 | type conversion |
|
||||
nodes
|
||||
| test.go:13:12:13:16 | implicit dereference : URL | semmle.label | implicit dereference : URL |
|
||||
| test.go:13:12:13:16 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
|
||||
| test.go:13:12:13:21 | selection of Path : string | semmle.label | selection of Path : string |
|
||||
| test.go:21:11:21:24 | type conversion | semmle.label | type conversion |
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
edges
|
||||
| test.go:170:11:170:32 | call to Param : string | test.go:171:20:171:24 | param |
|
||||
| test.go:176:11:176:32 | call to Param : string | test.go:180:20:180:28 | ...+... |
|
||||
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:188:10:188:26 | selection of URL : pointer type |
|
||||
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:188:10:188:26 | selection of URL : pointer type |
|
||||
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:191:21:191:32 | call to String |
|
||||
| test.go:188:10:188:26 | selection of URL : pointer type | test.go:191:21:191:32 | call to String |
|
||||
nodes
|
||||
|
||||
@@ -7,41 +7,10 @@ edges
|
||||
| test.go:43:9:43:34 | call to FormValue : string | test.go:44:16:44:18 | val |
|
||||
| test.go:49:2:49:30 | ... := ...[0] : Values | test.go:50:16:50:37 | index expression |
|
||||
| test.go:55:2:55:46 | ... := ...[0] : pointer type | test.go:59:20:59:25 | buffer |
|
||||
| test.go:64:2:64:31 | ... := ...[0] : pointer type | test.go:65:16:65:19 | implicit dereference : Form |
|
||||
| test.go:64:2:64:31 | ... := ...[0] : pointer type | test.go:65:16:65:25 | selection of Value : map type |
|
||||
| test.go:64:2:64:31 | ... := ...[0] : pointer type | test.go:65:16:65:38 | index expression : slice type |
|
||||
| test.go:64:2:64:31 | ... := ...[0] : pointer type | test.go:65:16:65:41 | index expression |
|
||||
| test.go:65:16:65:19 | implicit dereference : Form | test.go:65:16:65:19 | implicit dereference : Form |
|
||||
| test.go:65:16:65:19 | implicit dereference : Form | test.go:65:16:65:25 | selection of Value : map type |
|
||||
| test.go:65:16:65:19 | implicit dereference : Form | test.go:65:16:65:38 | index expression : slice type |
|
||||
| test.go:65:16:65:19 | implicit dereference : Form | test.go:65:16:65:41 | index expression |
|
||||
| test.go:65:16:65:25 | selection of Value : map type | test.go:65:16:65:38 | index expression : slice type |
|
||||
| test.go:65:16:65:25 | selection of Value : map type | test.go:65:16:65:41 | index expression |
|
||||
| test.go:65:16:65:38 | index expression : slice type | test.go:65:16:65:41 | index expression |
|
||||
| test.go:70:2:70:31 | ... := ...[0] : pointer type | test.go:71:16:71:19 | implicit dereference : Form |
|
||||
| test.go:70:2:70:31 | ... := ...[0] : pointer type | test.go:71:16:71:24 | selection of File : map type |
|
||||
| test.go:70:2:70:31 | ... := ...[0] : pointer type | test.go:71:16:71:40 | index expression : slice type |
|
||||
| test.go:70:2:70:31 | ... := ...[0] : pointer type | test.go:75:20:75:25 | buffer |
|
||||
| test.go:71:16:71:19 | implicit dereference : Form | test.go:71:16:71:19 | implicit dereference : Form |
|
||||
| test.go:71:16:71:19 | implicit dereference : Form | test.go:71:16:71:24 | selection of File : map type |
|
||||
| test.go:71:16:71:19 | implicit dereference : Form | test.go:71:16:71:40 | index expression : slice type |
|
||||
| test.go:71:16:71:19 | implicit dereference : Form | test.go:75:20:75:25 | buffer |
|
||||
| test.go:71:16:71:24 | selection of File : map type | test.go:71:16:71:40 | index expression : slice type |
|
||||
| test.go:71:16:71:24 | selection of File : map type | test.go:75:20:75:25 | buffer |
|
||||
| test.go:71:16:71:40 | index expression : slice type | test.go:75:20:75:25 | buffer |
|
||||
| test.go:80:2:80:32 | ... := ...[0] : pointer type | test.go:81:16:81:18 | implicit dereference : Cookie |
|
||||
| test.go:80:2:80:32 | ... := ...[0] : pointer type | test.go:81:16:81:24 | selection of Value |
|
||||
| test.go:81:16:81:18 | implicit dereference : Cookie | test.go:81:16:81:18 | implicit dereference : Cookie |
|
||||
| test.go:81:16:81:18 | implicit dereference : Cookie | test.go:81:16:81:24 | selection of Value |
|
||||
| test.go:86:13:86:25 | call to Cookies : slice type | test.go:87:16:87:25 | implicit dereference : Cookie |
|
||||
| test.go:86:13:86:25 | call to Cookies : slice type | test.go:87:16:87:25 | index expression : pointer type |
|
||||
| test.go:86:13:86:25 | call to Cookies : slice type | test.go:87:16:87:31 | selection of Value |
|
||||
| test.go:87:16:87:25 | implicit dereference : Cookie | test.go:87:16:87:25 | implicit dereference : Cookie |
|
||||
| test.go:87:16:87:25 | implicit dereference : Cookie | test.go:87:16:87:25 | index expression : pointer type |
|
||||
| test.go:87:16:87:25 | implicit dereference : Cookie | test.go:87:16:87:31 | selection of Value |
|
||||
| test.go:87:16:87:25 | index expression : pointer type | test.go:87:16:87:25 | implicit dereference : Cookie |
|
||||
| test.go:87:16:87:25 | index expression : pointer type | test.go:87:16:87:25 | index expression : pointer type |
|
||||
| test.go:87:16:87:25 | index expression : pointer type | test.go:87:16:87:31 | selection of Value |
|
||||
| test.go:97:11:97:15 | &... : pointer type | test.go:98:16:98:21 | selection of s |
|
||||
| test.go:111:21:111:42 | call to Param : string | test.go:112:16:112:42 | type assertion |
|
||||
| test.go:122:11:122:32 | call to Param : string | test.go:123:16:123:20 | param |
|
||||
@@ -67,21 +36,12 @@ nodes
|
||||
| test.go:55:2:55:46 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| test.go:59:20:59:25 | buffer | semmle.label | buffer |
|
||||
| test.go:64:2:64:31 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| test.go:65:16:65:19 | implicit dereference : Form | semmle.label | implicit dereference : Form |
|
||||
| test.go:65:16:65:25 | selection of Value : map type | semmle.label | selection of Value : map type |
|
||||
| test.go:65:16:65:38 | index expression : slice type | semmle.label | index expression : slice type |
|
||||
| test.go:65:16:65:41 | index expression | semmle.label | index expression |
|
||||
| test.go:70:2:70:31 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| test.go:71:16:71:19 | implicit dereference : Form | semmle.label | implicit dereference : Form |
|
||||
| test.go:71:16:71:24 | selection of File : map type | semmle.label | selection of File : map type |
|
||||
| test.go:71:16:71:40 | index expression : slice type | semmle.label | index expression : slice type |
|
||||
| test.go:75:20:75:25 | buffer | semmle.label | buffer |
|
||||
| test.go:80:2:80:32 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| test.go:81:16:81:18 | implicit dereference : Cookie | semmle.label | implicit dereference : Cookie |
|
||||
| test.go:81:16:81:24 | selection of Value | semmle.label | selection of Value |
|
||||
| test.go:86:13:86:25 | call to Cookies : slice type | semmle.label | call to Cookies : slice type |
|
||||
| test.go:87:16:87:25 | implicit dereference : Cookie | semmle.label | implicit dereference : Cookie |
|
||||
| test.go:87:16:87:25 | index expression : pointer type | semmle.label | index expression : pointer type |
|
||||
| test.go:87:16:87:31 | selection of Value | semmle.label | selection of Value |
|
||||
| test.go:97:11:97:15 | &... : pointer type | semmle.label | &... : pointer type |
|
||||
| test.go:98:16:98:21 | selection of s | semmle.label | selection of s |
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
edges
|
||||
| EndToEnd.go:94:20:94:27 | implicit dereference : Params | EndToEnd.go:94:20:94:27 | implicit dereference : Params |
|
||||
| EndToEnd.go:94:20:94:27 | implicit dereference : Params | EndToEnd.go:94:20:94:27 | selection of Params : pointer type |
|
||||
| EndToEnd.go:94:20:94:27 | implicit dereference : Params | EndToEnd.go:94:20:94:49 | call to Get |
|
||||
| EndToEnd.go:94:20:94:27 | selection of Params : pointer type | EndToEnd.go:94:20:94:27 | implicit dereference : Params |
|
||||
| EndToEnd.go:94:20:94:27 | selection of Params : pointer type | EndToEnd.go:94:20:94:27 | selection of Params : pointer type |
|
||||
| EndToEnd.go:94:20:94:27 | selection of Params : pointer type | EndToEnd.go:94:20:94:49 | call to Get |
|
||||
nodes
|
||||
| EndToEnd.go:94:20:94:27 | implicit dereference : Params | semmle.label | implicit dereference : Params |
|
||||
| EndToEnd.go:94:20:94:27 | selection of Params : pointer type | semmle.label | selection of Params : pointer type |
|
||||
| EndToEnd.go:94:20:94:49 | call to Get | semmle.label | call to Get |
|
||||
subpaths
|
||||
|
||||
@@ -1,48 +1,18 @@
|
||||
edges
|
||||
| EndToEnd.go:36:18:36:25 | implicit dereference : Params | EndToEnd.go:36:18:36:25 | implicit dereference : Params |
|
||||
| EndToEnd.go:36:18:36:25 | implicit dereference : Params | EndToEnd.go:36:18:36:25 | selection of Params : pointer type |
|
||||
| EndToEnd.go:36:18:36:25 | implicit dereference : Params | EndToEnd.go:37:24:37:26 | buf |
|
||||
| EndToEnd.go:36:18:36:25 | selection of Params : pointer type | EndToEnd.go:36:18:36:25 | implicit dereference : Params |
|
||||
| EndToEnd.go:36:18:36:25 | selection of Params : pointer type | EndToEnd.go:36:18:36:25 | selection of Params : pointer type |
|
||||
| EndToEnd.go:36:18:36:25 | selection of Params : pointer type | EndToEnd.go:37:24:37:26 | buf |
|
||||
| EndToEnd.go:69:22:69:29 | implicit dereference : Params | EndToEnd.go:69:22:69:29 | implicit dereference : Params |
|
||||
| EndToEnd.go:69:22:69:29 | implicit dereference : Params | EndToEnd.go:69:22:69:29 | selection of Params : pointer type |
|
||||
| EndToEnd.go:69:22:69:29 | implicit dereference : Params | EndToEnd.go:69:22:69:51 | call to Get |
|
||||
| EndToEnd.go:69:22:69:29 | selection of Params : pointer type | EndToEnd.go:69:22:69:29 | implicit dereference : Params |
|
||||
| EndToEnd.go:69:22:69:29 | selection of Params : pointer type | EndToEnd.go:69:22:69:29 | selection of Params : pointer type |
|
||||
| EndToEnd.go:69:22:69:29 | selection of Params : pointer type | EndToEnd.go:69:22:69:51 | call to Get |
|
||||
| Revel.go:70:22:70:29 | implicit dereference : Params | Revel.go:70:22:70:29 | implicit dereference : Params |
|
||||
| Revel.go:70:22:70:29 | implicit dereference : Params | Revel.go:70:22:70:29 | selection of Params : pointer type |
|
||||
| Revel.go:70:22:70:29 | implicit dereference : Params | Revel.go:70:22:70:35 | selection of Query |
|
||||
| Revel.go:70:22:70:29 | selection of Params : pointer type | Revel.go:70:22:70:29 | implicit dereference : Params |
|
||||
| Revel.go:70:22:70:29 | selection of Params : pointer type | Revel.go:70:22:70:29 | selection of Params : pointer type |
|
||||
| Revel.go:70:22:70:29 | selection of Params : pointer type | Revel.go:70:22:70:35 | selection of Query |
|
||||
| examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL | examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL |
|
||||
| examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL | examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type |
|
||||
| examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL | examples/booking/app/init.go:36:44:36:53 | selection of Path |
|
||||
| examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type | examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL |
|
||||
| examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type | examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type |
|
||||
| examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type | examples/booking/app/init.go:36:44:36:53 | selection of Path |
|
||||
| examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL | examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL |
|
||||
| examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL | examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type |
|
||||
| examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL | examples/booking/app/init.go:40:49:40:58 | selection of Path |
|
||||
| examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type | examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL |
|
||||
| examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type | examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type |
|
||||
| examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type | examples/booking/app/init.go:40:49:40:58 | selection of Path |
|
||||
nodes
|
||||
| EndToEnd.go:36:18:36:25 | implicit dereference : Params | semmle.label | implicit dereference : Params |
|
||||
| EndToEnd.go:36:18:36:25 | selection of Params : pointer type | semmle.label | selection of Params : pointer type |
|
||||
| EndToEnd.go:37:24:37:26 | buf | semmle.label | buf |
|
||||
| EndToEnd.go:69:22:69:29 | implicit dereference : Params | semmle.label | implicit dereference : Params |
|
||||
| EndToEnd.go:69:22:69:29 | selection of Params : pointer type | semmle.label | selection of Params : pointer type |
|
||||
| EndToEnd.go:69:22:69:51 | call to Get | semmle.label | call to Get |
|
||||
| Revel.go:70:22:70:29 | implicit dereference : Params | semmle.label | implicit dereference : Params |
|
||||
| Revel.go:70:22:70:29 | selection of Params : pointer type | semmle.label | selection of Params : pointer type |
|
||||
| Revel.go:70:22:70:35 | selection of Query | semmle.label | selection of Query |
|
||||
| examples/booking/app/init.go:36:44:36:48 | implicit dereference : URL | semmle.label | implicit dereference : URL |
|
||||
| examples/booking/app/init.go:36:44:36:48 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
|
||||
| examples/booking/app/init.go:36:44:36:53 | selection of Path | semmle.label | selection of Path |
|
||||
| examples/booking/app/init.go:40:49:40:53 | implicit dereference : URL | semmle.label | implicit dereference : URL |
|
||||
| examples/booking/app/init.go:40:49:40:53 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
|
||||
| examples/booking/app/init.go:40:49:40:58 | selection of Path | semmle.label | selection of Path |
|
||||
subpaths
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
edges
|
||||
| EndToEnd.go:58:18:58:25 | implicit dereference : Params | EndToEnd.go:58:18:58:25 | implicit dereference : Params |
|
||||
| EndToEnd.go:58:18:58:25 | implicit dereference : Params | EndToEnd.go:58:18:58:25 | selection of Params : pointer type |
|
||||
| EndToEnd.go:58:18:58:25 | implicit dereference : Params | EndToEnd.go:58:18:58:47 | call to Get |
|
||||
| EndToEnd.go:58:18:58:25 | selection of Params : pointer type | EndToEnd.go:58:18:58:25 | implicit dereference : Params |
|
||||
| EndToEnd.go:58:18:58:25 | selection of Params : pointer type | EndToEnd.go:58:18:58:25 | selection of Params : pointer type |
|
||||
| EndToEnd.go:58:18:58:25 | selection of Params : pointer type | EndToEnd.go:58:18:58:47 | call to Get |
|
||||
| EndToEnd.go:64:26:64:33 | implicit dereference : Params | EndToEnd.go:64:26:64:33 | implicit dereference : Params |
|
||||
| EndToEnd.go:64:26:64:33 | implicit dereference : Params | EndToEnd.go:64:26:64:33 | selection of Params : pointer type |
|
||||
| EndToEnd.go:64:26:64:33 | implicit dereference : Params | EndToEnd.go:64:26:64:55 | call to Get |
|
||||
| EndToEnd.go:64:26:64:33 | selection of Params : pointer type | EndToEnd.go:64:26:64:33 | implicit dereference : Params |
|
||||
| EndToEnd.go:64:26:64:33 | selection of Params : pointer type | EndToEnd.go:64:26:64:33 | selection of Params : pointer type |
|
||||
| EndToEnd.go:64:26:64:33 | selection of Params : pointer type | EndToEnd.go:64:26:64:55 | call to Get |
|
||||
nodes
|
||||
| EndToEnd.go:58:18:58:25 | implicit dereference : Params | semmle.label | implicit dereference : Params |
|
||||
| EndToEnd.go:58:18:58:25 | selection of Params : pointer type | semmle.label | selection of Params : pointer type |
|
||||
| EndToEnd.go:58:18:58:47 | call to Get | semmle.label | call to Get |
|
||||
| EndToEnd.go:64:26:64:33 | implicit dereference : Params | semmle.label | implicit dereference : Params |
|
||||
| EndToEnd.go:64:26:64:33 | selection of Params : pointer type | semmle.label | selection of Params : pointer type |
|
||||
| EndToEnd.go:64:26:64:55 | call to Get | semmle.label | call to Get |
|
||||
subpaths
|
||||
|
||||
@@ -13,13 +13,13 @@ class Link extends TaintTracking::FunctionModel {
|
||||
}
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node source, DataFlow::CallNode call) {
|
||||
predicate callResultisSource(DataFlow::Node source, DataFlow::CallNode call) {
|
||||
exists(Function fn | fn.hasQualifiedName(_, "newSource") |
|
||||
call = fn.getACall() and source = call.getResult()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, DataFlow::CallNode call) {
|
||||
predicate callArgumentisSink(DataFlow::Node sink, DataFlow::CallNode call) {
|
||||
exists(Function fn | fn.hasQualifiedName(_, "sink") |
|
||||
call = fn.getACall() and sink = call.getArgument(1)
|
||||
)
|
||||
@@ -28,9 +28,9 @@ predicate isSink(DataFlow::Node sink, DataFlow::CallNode call) {
|
||||
class FlowConf extends TaintTracking::Configuration {
|
||||
FlowConf() { this = "FlowConf" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
override predicate isSource(DataFlow::Node source) { callResultisSource(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
|
||||
override predicate isSink(DataFlow::Node sink) { callArgumentisSink(sink, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,8 +43,8 @@ predicate flowsToSink(DataFlow::CallNode sourceCall) {
|
||||
|
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
(
|
||||
isSource(source.getNode(), sourceCall) and
|
||||
isSink(sink.getNode(), sinkCall) and
|
||||
callResultisSource(source.getNode(), sourceCall) and
|
||||
callArgumentisSink(sink.getNode(), sinkCall) and
|
||||
sourceCall.getArgument(0).getIntValue() = sinkCall.getArgument(0).getIntValue()
|
||||
)
|
||||
)
|
||||
@@ -52,5 +52,5 @@ predicate flowsToSink(DataFlow::CallNode sourceCall) {
|
||||
|
||||
/* Show only flow sources that DON'T flow to their dedicated sink. */
|
||||
from DataFlow::CallNode sourceCall
|
||||
where isSource(_, sourceCall) and not flowsToSink(sourceCall)
|
||||
where callResultisSource(_, sourceCall) and not flowsToSink(sourceCall)
|
||||
select sourceCall, "No flow to its sink"
|
||||
|
||||
@@ -1,61 +1,26 @@
|
||||
edges
|
||||
| test.go:10:2:10:42 | ... := ...[0] : pointer type | test.go:14:15:14:55 | type conversion |
|
||||
| test.go:10:2:10:42 | ... := ...[0] : pointer type | test.go:14:42:14:47 | implicit dereference : Cookie |
|
||||
| test.go:14:42:14:47 | implicit dereference : Cookie | test.go:14:15:14:55 | type conversion |
|
||||
| test.go:14:42:14:47 | implicit dereference : Cookie | test.go:14:42:14:47 | implicit dereference : Cookie |
|
||||
| test.go:16:24:16:35 | selection of Body : ReadCloser | test.go:17:15:17:31 | type conversion |
|
||||
| test.go:16:24:16:35 | selection of Body : ReadCloser | test.go:17:22:17:25 | implicit dereference : Node |
|
||||
| test.go:16:24:16:35 | selection of Body : ReadCloser | test.go:28:22:28:25 | node |
|
||||
| test.go:17:22:17:25 | implicit dereference : Node | test.go:17:15:17:31 | type conversion |
|
||||
| test.go:17:22:17:25 | implicit dereference : Node | test.go:17:22:17:25 | implicit dereference : Node |
|
||||
| test.go:17:22:17:25 | implicit dereference : Node | test.go:28:22:28:25 | node |
|
||||
| test.go:19:36:19:47 | selection of Body : ReadCloser | test.go:20:15:20:32 | type conversion |
|
||||
| test.go:19:36:19:47 | selection of Body : ReadCloser | test.go:20:22:20:26 | implicit dereference : Node |
|
||||
| test.go:20:22:20:26 | implicit dereference : Node | test.go:20:15:20:32 | type conversion |
|
||||
| test.go:20:22:20:26 | implicit dereference : Node | test.go:20:22:20:26 | implicit dereference : Node |
|
||||
| test.go:22:33:22:44 | selection of Body : ReadCloser | test.go:23:15:23:35 | type conversion |
|
||||
| test.go:22:33:22:44 | selection of Body : ReadCloser | test.go:23:22:23:29 | implicit dereference : Node |
|
||||
| test.go:22:33:22:44 | selection of Body : ReadCloser | test.go:23:22:23:29 | index expression : pointer type |
|
||||
| test.go:23:22:23:29 | implicit dereference : Node | test.go:23:15:23:35 | type conversion |
|
||||
| test.go:23:22:23:29 | implicit dereference : Node | test.go:23:22:23:29 | implicit dereference : Node |
|
||||
| test.go:23:22:23:29 | implicit dereference : Node | test.go:23:22:23:29 | index expression : pointer type |
|
||||
| test.go:23:22:23:29 | index expression : pointer type | test.go:23:15:23:35 | type conversion |
|
||||
| test.go:23:22:23:29 | index expression : pointer type | test.go:23:22:23:29 | implicit dereference : Node |
|
||||
| test.go:23:22:23:29 | index expression : pointer type | test.go:23:22:23:29 | index expression : pointer type |
|
||||
| test.go:25:45:25:56 | selection of Body : ReadCloser | test.go:26:15:26:36 | type conversion |
|
||||
| test.go:25:45:25:56 | selection of Body : ReadCloser | test.go:26:22:26:30 | implicit dereference : Node |
|
||||
| test.go:25:45:25:56 | selection of Body : ReadCloser | test.go:26:22:26:30 | index expression : pointer type |
|
||||
| test.go:26:22:26:30 | implicit dereference : Node | test.go:26:15:26:36 | type conversion |
|
||||
| test.go:26:22:26:30 | implicit dereference : Node | test.go:26:22:26:30 | implicit dereference : Node |
|
||||
| test.go:26:22:26:30 | implicit dereference : Node | test.go:26:22:26:30 | index expression : pointer type |
|
||||
| test.go:26:22:26:30 | index expression : pointer type | test.go:26:15:26:36 | type conversion |
|
||||
| test.go:26:22:26:30 | index expression : pointer type | test.go:26:22:26:30 | implicit dereference : Node |
|
||||
| test.go:26:22:26:30 | index expression : pointer type | test.go:26:22:26:30 | index expression : pointer type |
|
||||
| test.go:30:33:30:44 | selection of Body : ReadCloser | test.go:31:15:31:34 | call to Buffered |
|
||||
| test.go:30:33:30:44 | selection of Body : ReadCloser | test.go:32:15:32:29 | call to Raw |
|
||||
| test.go:30:33:30:44 | selection of Body : ReadCloser | test.go:34:15:34:19 | value |
|
||||
| test.go:30:33:30:44 | selection of Body : ReadCloser | test.go:35:15:35:30 | call to Text |
|
||||
| test.go:30:33:30:44 | selection of Body : ReadCloser | test.go:36:15:36:44 | type conversion |
|
||||
| test.go:30:33:30:44 | selection of Body : ReadCloser | test.go:36:22:36:38 | call to Token : Token |
|
||||
| test.go:36:22:36:38 | call to Token : Token | test.go:36:15:36:44 | type conversion |
|
||||
nodes
|
||||
| test.go:10:2:10:42 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| test.go:14:15:14:55 | type conversion | semmle.label | type conversion |
|
||||
| test.go:14:42:14:47 | implicit dereference : Cookie | semmle.label | implicit dereference : Cookie |
|
||||
| test.go:16:24:16:35 | selection of Body : ReadCloser | semmle.label | selection of Body : ReadCloser |
|
||||
| test.go:17:15:17:31 | type conversion | semmle.label | type conversion |
|
||||
| test.go:17:22:17:25 | implicit dereference : Node | semmle.label | implicit dereference : Node |
|
||||
| test.go:19:36:19:47 | selection of Body : ReadCloser | semmle.label | selection of Body : ReadCloser |
|
||||
| test.go:20:15:20:32 | type conversion | semmle.label | type conversion |
|
||||
| test.go:20:22:20:26 | implicit dereference : Node | semmle.label | implicit dereference : Node |
|
||||
| test.go:22:33:22:44 | selection of Body : ReadCloser | semmle.label | selection of Body : ReadCloser |
|
||||
| test.go:23:15:23:35 | type conversion | semmle.label | type conversion |
|
||||
| test.go:23:22:23:29 | implicit dereference : Node | semmle.label | implicit dereference : Node |
|
||||
| test.go:23:22:23:29 | index expression : pointer type | semmle.label | index expression : pointer type |
|
||||
| test.go:25:45:25:56 | selection of Body : ReadCloser | semmle.label | selection of Body : ReadCloser |
|
||||
| test.go:26:15:26:36 | type conversion | semmle.label | type conversion |
|
||||
| test.go:26:22:26:30 | implicit dereference : Node | semmle.label | implicit dereference : Node |
|
||||
| test.go:26:22:26:30 | index expression : pointer type | semmle.label | index expression : pointer type |
|
||||
| test.go:28:22:28:25 | node | semmle.label | node |
|
||||
| test.go:30:33:30:44 | selection of Body : ReadCloser | semmle.label | selection of Body : ReadCloser |
|
||||
| test.go:31:15:31:34 | call to Buffered | semmle.label | call to Buffered |
|
||||
@@ -63,7 +28,6 @@ nodes
|
||||
| test.go:34:15:34:19 | value | semmle.label | value |
|
||||
| test.go:35:15:35:30 | call to Text | semmle.label | call to Text |
|
||||
| test.go:36:15:36:44 | type conversion | semmle.label | type conversion |
|
||||
| test.go:36:22:36:38 | call to Token : Token | semmle.label | call to Token : Token |
|
||||
subpaths
|
||||
#select
|
||||
| test.go:14:15:14:55 | type conversion | test.go:10:2:10:42 | ... := ...[0] : pointer type | test.go:14:15:14:55 | type conversion | Cross-site scripting vulnerability due to $@. | test.go:10:2:10:42 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | |
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
edges
|
||||
| TaintedPath.go:13:18:13:22 | selection of URL : pointer type | TaintedPath.go:16:29:16:40 | tainted_path |
|
||||
| TaintedPath.go:13:18:13:22 | selection of URL : pointer type | TaintedPath.go:20:28:20:69 | call to Join |
|
||||
| tst.go:14:2:14:39 | ... := ...[1] : pointer type | tst.go:17:41:17:47 | implicit dereference : FileHeader |
|
||||
| tst.go:14:2:14:39 | ... := ...[1] : pointer type | tst.go:17:41:17:56 | selection of Filename |
|
||||
| tst.go:17:41:17:47 | implicit dereference : FileHeader | tst.go:17:41:17:47 | implicit dereference : FileHeader |
|
||||
| tst.go:17:41:17:47 | implicit dereference : FileHeader | tst.go:17:41:17:56 | selection of Filename |
|
||||
nodes
|
||||
| TaintedPath.go:13:18:13:22 | selection of URL : pointer type | semmle.label | selection of URL : pointer type |
|
||||
| TaintedPath.go:16:29:16:40 | tainted_path | semmle.label | tainted_path |
|
||||
| TaintedPath.go:20:28:20:69 | call to Join | semmle.label | call to Join |
|
||||
| tst.go:14:2:14:39 | ... := ...[1] : pointer type | semmle.label | ... := ...[1] : pointer type |
|
||||
| tst.go:17:41:17:47 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| tst.go:17:41:17:56 | selection of Filename | semmle.label | selection of Filename |
|
||||
subpaths
|
||||
#select
|
||||
|
||||
@@ -1,55 +1,23 @@
|
||||
edges
|
||||
| UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate : string | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join |
|
||||
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] : pointer type | UnsafeUnzipSymlinkGood.go:76:24:76:29 | implicit dereference : Header |
|
||||
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] : pointer type | UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname : string |
|
||||
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] : pointer type | UnsafeUnzipSymlinkGood.go:76:70:76:75 | implicit dereference : Header |
|
||||
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] : pointer type | UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name : string |
|
||||
| UnsafeUnzipSymlinkGood.go:76:24:76:29 | implicit dereference : Header | UnsafeUnzipSymlinkGood.go:76:24:76:29 | implicit dereference : Header |
|
||||
| UnsafeUnzipSymlinkGood.go:76:24:76:29 | implicit dereference : Header | UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname : string |
|
||||
| UnsafeUnzipSymlinkGood.go:76:24:76:29 | implicit dereference : Header | UnsafeUnzipSymlinkGood.go:76:70:76:75 | implicit dereference : Header |
|
||||
| UnsafeUnzipSymlinkGood.go:76:24:76:29 | implicit dereference : Header | UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name : string |
|
||||
| UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname : string | UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate : string |
|
||||
| UnsafeUnzipSymlinkGood.go:76:70:76:75 | implicit dereference : Header | UnsafeUnzipSymlinkGood.go:76:24:76:29 | implicit dereference : Header |
|
||||
| UnsafeUnzipSymlinkGood.go:76:70:76:75 | implicit dereference : Header | UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname : string |
|
||||
| UnsafeUnzipSymlinkGood.go:76:70:76:75 | implicit dereference : Header | UnsafeUnzipSymlinkGood.go:76:70:76:75 | implicit dereference : Header |
|
||||
| UnsafeUnzipSymlinkGood.go:76:70:76:75 | implicit dereference : Header | UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name : string |
|
||||
| UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name : string | UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate : string |
|
||||
| ZipSlip.go:11:2:15:2 | range statement[1] : pointer type | ZipSlip.go:12:24:12:24 | implicit dereference : File |
|
||||
| ZipSlip.go:11:2:15:2 | range statement[1] : pointer type | ZipSlip.go:12:24:12:24 | implicit read of field FileHeader : FileHeader |
|
||||
| ZipSlip.go:11:2:15:2 | range statement[1] : pointer type | ZipSlip.go:14:20:14:20 | p |
|
||||
| ZipSlip.go:12:24:12:24 | implicit dereference : File | ZipSlip.go:12:24:12:24 | implicit dereference : File |
|
||||
| ZipSlip.go:12:24:12:24 | implicit dereference : File | ZipSlip.go:12:24:12:24 | implicit read of field FileHeader : FileHeader |
|
||||
| ZipSlip.go:12:24:12:24 | implicit dereference : File | ZipSlip.go:14:20:14:20 | p |
|
||||
| ZipSlip.go:12:24:12:24 | implicit read of field FileHeader : FileHeader | ZipSlip.go:14:20:14:20 | p |
|
||||
| tarslip.go:15:2:15:30 | ... := ...[0] : pointer type | tarslip.go:16:14:16:34 | call to Dir |
|
||||
| tarslip.go:15:2:15:30 | ... := ...[0] : pointer type | tarslip.go:16:23:16:28 | implicit dereference : Header |
|
||||
| tarslip.go:16:23:16:28 | implicit dereference : Header | tarslip.go:16:14:16:34 | call to Dir |
|
||||
| tarslip.go:16:23:16:28 | implicit dereference : Header | tarslip.go:16:23:16:28 | implicit dereference : Header |
|
||||
| tst.go:23:2:43:2 | range statement[1] : pointer type | tst.go:24:11:24:11 | implicit dereference : File |
|
||||
| tst.go:23:2:43:2 | range statement[1] : pointer type | tst.go:24:11:24:11 | implicit read of field FileHeader : FileHeader |
|
||||
| tst.go:23:2:43:2 | range statement[1] : pointer type | tst.go:29:20:29:23 | path |
|
||||
| tst.go:24:11:24:11 | implicit dereference : File | tst.go:24:11:24:11 | implicit dereference : File |
|
||||
| tst.go:24:11:24:11 | implicit dereference : File | tst.go:24:11:24:11 | implicit read of field FileHeader : FileHeader |
|
||||
| tst.go:24:11:24:11 | implicit dereference : File | tst.go:29:20:29:23 | path |
|
||||
| tst.go:24:11:24:11 | implicit read of field FileHeader : FileHeader | tst.go:29:20:29:23 | path |
|
||||
nodes
|
||||
| UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate : string | semmle.label | definition of candidate : string |
|
||||
| UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | semmle.label | call to Join |
|
||||
| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| UnsafeUnzipSymlinkGood.go:76:24:76:29 | implicit dereference : Header | semmle.label | implicit dereference : Header |
|
||||
| UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname : string | semmle.label | selection of Linkname : string |
|
||||
| UnsafeUnzipSymlinkGood.go:76:70:76:75 | implicit dereference : Header | semmle.label | implicit dereference : Header |
|
||||
| UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name : string | semmle.label | selection of Name : string |
|
||||
| ZipSlip.go:11:2:15:2 | range statement[1] : pointer type | semmle.label | range statement[1] : pointer type |
|
||||
| ZipSlip.go:12:24:12:24 | implicit dereference : File | semmle.label | implicit dereference : File |
|
||||
| ZipSlip.go:12:24:12:24 | implicit read of field FileHeader : FileHeader | semmle.label | implicit read of field FileHeader : FileHeader |
|
||||
| ZipSlip.go:14:20:14:20 | p | semmle.label | p |
|
||||
| tarslip.go:15:2:15:30 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| tarslip.go:16:14:16:34 | call to Dir | semmle.label | call to Dir |
|
||||
| tarslip.go:16:23:16:28 | implicit dereference : Header | semmle.label | implicit dereference : Header |
|
||||
| tst.go:23:2:43:2 | range statement[1] : pointer type | semmle.label | range statement[1] : pointer type |
|
||||
| tst.go:24:11:24:11 | implicit dereference : File | semmle.label | implicit dereference : File |
|
||||
| tst.go:24:11:24:11 | implicit read of field FileHeader : FileHeader | semmle.label | implicit read of field FileHeader : FileHeader |
|
||||
| tst.go:29:20:29:23 | path | semmle.label | path |
|
||||
subpaths
|
||||
#select
|
||||
|
||||
@@ -9,9 +9,6 @@ edges
|
||||
| reflectedxsstest.go:27:2:27:38 | ... := ...[0] : pointer type | reflectedxsstest.go:28:10:28:57 | type conversion |
|
||||
| reflectedxsstest.go:31:2:31:44 | ... := ...[0] : File | reflectedxsstest.go:33:10:33:57 | type conversion |
|
||||
| reflectedxsstest.go:31:2:31:44 | ... := ...[1] : pointer type | reflectedxsstest.go:34:10:34:62 | type conversion |
|
||||
| reflectedxsstest.go:31:2:31:44 | ... := ...[1] : pointer type | reflectedxsstest.go:34:46:34:51 | implicit dereference : FileHeader |
|
||||
| reflectedxsstest.go:34:46:34:51 | implicit dereference : FileHeader | reflectedxsstest.go:34:10:34:62 | type conversion |
|
||||
| reflectedxsstest.go:34:46:34:51 | implicit dereference : FileHeader | reflectedxsstest.go:34:46:34:51 | implicit dereference : FileHeader |
|
||||
| reflectedxsstest.go:38:2:38:35 | ... := ...[0] : pointer type | reflectedxsstest.go:44:10:44:55 | type conversion |
|
||||
| reflectedxsstest.go:38:2:38:35 | ... := ...[0] : pointer type | reflectedxsstest.go:45:10:45:18 | byteSlice |
|
||||
| reflectedxsstest.go:51:14:51:18 | selection of URL : pointer type | reflectedxsstest.go:54:11:54:21 | type conversion |
|
||||
@@ -44,7 +41,6 @@ nodes
|
||||
| reflectedxsstest.go:31:2:31:44 | ... := ...[1] : pointer type | semmle.label | ... := ...[1] : pointer type |
|
||||
| reflectedxsstest.go:33:10:33:57 | type conversion | semmle.label | type conversion |
|
||||
| reflectedxsstest.go:34:10:34:62 | type conversion | semmle.label | type conversion |
|
||||
| reflectedxsstest.go:34:46:34:51 | implicit dereference : FileHeader | semmle.label | implicit dereference : FileHeader |
|
||||
| reflectedxsstest.go:38:2:38:35 | ... := ...[0] : pointer type | semmle.label | ... := ...[0] : pointer type |
|
||||
| reflectedxsstest.go:44:10:44:55 | type conversion | semmle.label | type conversion |
|
||||
| reflectedxsstest.go:45:10:45:18 | byteSlice | semmle.label | byteSlice |
|
||||
|
||||
@@ -99,7 +99,7 @@ nodes
|
||||
| protos/query/query.pb.go:119:10:119:22 | selection of Description : string | semmle.label | selection of Description : string |
|
||||
| util.go:16:9:16:18 | selection of password : string | semmle.label | selection of password : string |
|
||||
subpaths
|
||||
| protobuf.go:14:14:14:18 | query [pointer, Description] : string | protos/query/query.pb.go:117:7:117:7 | definition of x [pointer, Description] : string | protos/query/query.pb.go:119:10:119:22 | selection of Description : string | protobuf.go:14:14:14:35 | call to GetDescription : string |
|
||||
| protobuf.go:14:14:14:18 | query [pointer, Description] : string | protos/query/query.pb.go:117:7:117:7 | definition of x [pointer, Description] : string | protos/query/query.pb.go:119:10:119:22 | selection of Description : string | protobuf.go:14:14:14:35 | call to GetDescription |
|
||||
#select
|
||||
| klog.go:22:15:22:20 | header | klog.go:20:30:20:37 | selection of Header : Header | klog.go:22:15:22:20 | header | $@ flows to a logging call. | klog.go:20:30:20:37 | selection of Header | Sensitive data returned by HTTP request headers |
|
||||
| klog.go:28:13:28:41 | call to Get | klog.go:28:13:28:20 | selection of Header : Header | klog.go:28:13:28:41 | call to Get | $@ flows to a logging call. | klog.go:28:13:28:20 | selection of Header | Sensitive data returned by HTTP request headers |
|
||||
|
||||
@@ -15,22 +15,8 @@ edges
|
||||
| UnsafeTLS.go:313:5:313:45 | selection of TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 : uint16 | UnsafeTLS.go:312:18:314:4 | slice literal |
|
||||
| UnsafeTLS.go:329:53:329:93 | selection of TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 : uint16 | UnsafeTLS.go:329:25:329:94 | call to append |
|
||||
| UnsafeTLS.go:334:13:334:38 | call to InsecureCipherSuites : slice type | UnsafeTLS.go:336:26:336:58 | call to append |
|
||||
| UnsafeTLS.go:334:13:334:38 | call to InsecureCipherSuites : slice type | UnsafeTLS.go:336:54:336:54 | implicit dereference : CipherSuite |
|
||||
| UnsafeTLS.go:336:54:336:54 | implicit dereference : CipherSuite | UnsafeTLS.go:336:26:336:58 | call to append |
|
||||
| UnsafeTLS.go:336:54:336:54 | implicit dereference : CipherSuite | UnsafeTLS.go:336:54:336:54 | implicit dereference : CipherSuite |
|
||||
| UnsafeTLS.go:342:13:342:38 | call to InsecureCipherSuites : slice type | UnsafeTLS.go:344:40:344:40 | implicit dereference : CipherSuite |
|
||||
| UnsafeTLS.go:342:13:342:38 | call to InsecureCipherSuites : slice type | UnsafeTLS.go:346:25:346:36 | cipherSuites |
|
||||
| UnsafeTLS.go:344:40:344:40 | implicit dereference : CipherSuite | UnsafeTLS.go:344:40:344:40 | implicit dereference : CipherSuite |
|
||||
| UnsafeTLS.go:344:40:344:40 | implicit dereference : CipherSuite | UnsafeTLS.go:346:25:346:36 | cipherSuites |
|
||||
| UnsafeTLS.go:351:13:351:38 | call to InsecureCipherSuites : slice type | UnsafeTLS.go:353:40:353:48 | implicit dereference : CipherSuite |
|
||||
| UnsafeTLS.go:351:13:351:38 | call to InsecureCipherSuites : slice type | UnsafeTLS.go:353:40:353:48 | index expression : pointer type |
|
||||
| UnsafeTLS.go:351:13:351:38 | call to InsecureCipherSuites : slice type | UnsafeTLS.go:355:25:355:36 | cipherSuites |
|
||||
| UnsafeTLS.go:353:40:353:48 | implicit dereference : CipherSuite | UnsafeTLS.go:353:40:353:48 | implicit dereference : CipherSuite |
|
||||
| UnsafeTLS.go:353:40:353:48 | implicit dereference : CipherSuite | UnsafeTLS.go:353:40:353:48 | index expression : pointer type |
|
||||
| UnsafeTLS.go:353:40:353:48 | implicit dereference : CipherSuite | UnsafeTLS.go:355:25:355:36 | cipherSuites |
|
||||
| UnsafeTLS.go:353:40:353:48 | index expression : pointer type | UnsafeTLS.go:353:40:353:48 | implicit dereference : CipherSuite |
|
||||
| UnsafeTLS.go:353:40:353:48 | index expression : pointer type | UnsafeTLS.go:353:40:353:48 | index expression : pointer type |
|
||||
| UnsafeTLS.go:353:40:353:48 | index expression : pointer type | UnsafeTLS.go:355:25:355:36 | cipherSuites |
|
||||
| UnsafeTLS.go:363:5:363:47 | selection of TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 : uint16 | UnsafeTLS.go:362:18:364:4 | slice literal |
|
||||
| UnsafeTLS.go:371:5:371:47 | selection of TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 : uint16 | UnsafeTLS.go:370:18:372:4 | slice literal |
|
||||
| UnsafeTLS.go:379:5:379:47 | selection of TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 : uint16 | UnsafeTLS.go:378:18:380:4 | slice literal |
|
||||
@@ -108,13 +94,9 @@ nodes
|
||||
| UnsafeTLS.go:329:53:329:93 | selection of TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 : uint16 | semmle.label | selection of TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 : uint16 |
|
||||
| UnsafeTLS.go:334:13:334:38 | call to InsecureCipherSuites : slice type | semmle.label | call to InsecureCipherSuites : slice type |
|
||||
| UnsafeTLS.go:336:26:336:58 | call to append | semmle.label | call to append |
|
||||
| UnsafeTLS.go:336:54:336:54 | implicit dereference : CipherSuite | semmle.label | implicit dereference : CipherSuite |
|
||||
| UnsafeTLS.go:342:13:342:38 | call to InsecureCipherSuites : slice type | semmle.label | call to InsecureCipherSuites : slice type |
|
||||
| UnsafeTLS.go:344:40:344:40 | implicit dereference : CipherSuite | semmle.label | implicit dereference : CipherSuite |
|
||||
| UnsafeTLS.go:346:25:346:36 | cipherSuites | semmle.label | cipherSuites |
|
||||
| UnsafeTLS.go:351:13:351:38 | call to InsecureCipherSuites : slice type | semmle.label | call to InsecureCipherSuites : slice type |
|
||||
| UnsafeTLS.go:353:40:353:48 | implicit dereference : CipherSuite | semmle.label | implicit dereference : CipherSuite |
|
||||
| UnsafeTLS.go:353:40:353:48 | index expression : pointer type | semmle.label | index expression : pointer type |
|
||||
| UnsafeTLS.go:355:25:355:36 | cipherSuites | semmle.label | cipherSuites |
|
||||
| UnsafeTLS.go:362:18:364:4 | slice literal | semmle.label | slice literal |
|
||||
| UnsafeTLS.go:363:5:363:47 | selection of TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 : uint16 | semmle.label | selection of TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 : uint16 |
|
||||
|
||||
@@ -42,8 +42,8 @@ nodes
|
||||
| main.go:87:9:87:14 | selection of Path : string | semmle.label | selection of Path : string |
|
||||
| main.go:91:25:91:39 | call to getTarget2 | semmle.label | call to getTarget2 |
|
||||
subpaths
|
||||
| main.go:11:37:11:44 | redirect : string | BadRedirectCheck.go:3:18:3:22 | definition of redir : string | BadRedirectCheck.go:5:10:5:14 | redir : string | main.go:11:25:11:45 | call to sanitizeUrl : string |
|
||||
| main.go:77:36:77:38 | url : string | main.go:68:17:68:24 | definition of redirect : string | main.go:73:9:73:28 | call to Clean : string | main.go:77:25:77:39 | call to getTarget1 : string |
|
||||
| main.go:11:37:11:44 | redirect : string | BadRedirectCheck.go:3:18:3:22 | definition of redir : string | BadRedirectCheck.go:5:10:5:14 | redir : string | main.go:11:25:11:45 | call to sanitizeUrl |
|
||||
| main.go:77:36:77:38 | url : string | main.go:68:17:68:24 | definition of redirect : string | main.go:73:9:73:28 | call to Clean : string | main.go:77:25:77:39 | call to getTarget1 |
|
||||
#select
|
||||
| BadRedirectCheck.go:4:23:4:37 | ...==... | BadRedirectCheck.go:3:18:3:22 | argument corresponding to redir : string | main.go:11:25:11:45 | call to sanitizeUrl | This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position. | BadRedirectCheck.go:3:18:3:22 | argument corresponding to redir | this value | main.go:11:25:11:45 | call to sanitizeUrl | redirect |
|
||||
| BadRedirectCheck.go:4:23:4:37 | ...==... | main.go:10:18:10:25 | argument corresponding to redirect : string | main.go:11:25:11:45 | call to sanitizeUrl | This is a check that $@, which flows into a $@, has a leading slash, but not that it does not have '/' or '\\' in its second position. | main.go:10:18:10:25 | argument corresponding to redirect | this value | main.go:11:25:11:45 | call to sanitizeUrl | redirect |
|
||||
|
||||
@@ -47,19 +47,13 @@ edges
|
||||
| stdlib.go:113:24:113:28 | selection of URL : pointer type | stdlib.go:113:24:113:37 | call to String |
|
||||
| stdlib.go:113:24:113:28 | selection of URL : pointer type | stdlib.go:113:24:113:37 | call to String |
|
||||
| stdlib.go:146:13:146:18 | selection of Form : Values | stdlib.go:152:23:152:28 | target |
|
||||
| stdlib.go:159:11:159:15 | selection of URL : pointer type | stdlib.go:159:11:159:15 | selection of URL : pointer type |
|
||||
| stdlib.go:159:11:159:15 | selection of URL : pointer type | stdlib.go:159:11:159:15 | selection of URL : pointer type |
|
||||
| stdlib.go:159:11:159:15 | selection of URL : pointer type | stdlib.go:162:24:162:35 | call to String |
|
||||
| stdlib.go:159:11:159:15 | selection of URL : pointer type | stdlib.go:162:24:162:35 | call to String |
|
||||
| stdlib.go:173:35:173:39 | selection of URL : pointer type | stdlib.go:173:24:173:52 | ...+... |
|
||||
| stdlib.go:173:35:173:39 | selection of URL : pointer type | stdlib.go:173:24:173:52 | ...+... |
|
||||
| stdlib.go:182:13:182:33 | call to FormValue : string | stdlib.go:184:23:184:28 | target |
|
||||
| stdlib.go:190:36:190:56 | call to FormValue : string | stdlib.go:192:23:192:28 | implicit dereference : URL |
|
||||
| stdlib.go:190:36:190:56 | call to FormValue : string | stdlib.go:192:23:192:33 | selection of Path |
|
||||
| stdlib.go:190:36:190:56 | call to FormValue : string | stdlib.go:194:23:194:42 | call to EscapedPath |
|
||||
| stdlib.go:192:23:192:28 | implicit dereference : URL | stdlib.go:192:23:192:28 | implicit dereference : URL |
|
||||
| stdlib.go:192:23:192:28 | implicit dereference : URL | stdlib.go:192:23:192:33 | selection of Path |
|
||||
| stdlib.go:192:23:192:28 | implicit dereference : URL | stdlib.go:194:23:194:42 | call to EscapedPath |
|
||||
nodes
|
||||
| OpenUrlRedirect.go:10:23:10:28 | selection of Form : Values | semmle.label | selection of Form : Values |
|
||||
| OpenUrlRedirect.go:10:23:10:42 | call to Get | semmle.label | call to Get |
|
||||
@@ -114,7 +108,6 @@ nodes
|
||||
| stdlib.go:182:13:182:33 | call to FormValue : string | semmle.label | call to FormValue : string |
|
||||
| stdlib.go:184:23:184:28 | target | semmle.label | target |
|
||||
| stdlib.go:190:36:190:56 | call to FormValue : string | semmle.label | call to FormValue : string |
|
||||
| stdlib.go:192:23:192:28 | implicit dereference : URL | semmle.label | implicit dereference : URL |
|
||||
| stdlib.go:192:23:192:33 | selection of Path | semmle.label | selection of Path |
|
||||
| stdlib.go:194:23:194:42 | call to EscapedPath | semmle.label | call to EscapedPath |
|
||||
subpaths
|
||||
|
||||
@@ -274,7 +274,7 @@ class DeadMethod extends Callable {
|
||||
|
||||
class RootdefCallable extends Callable {
|
||||
RootdefCallable() {
|
||||
this.fromSource() and
|
||||
this.getFile().isJavaSourceFile() and
|
||||
not this.(Method).overridesOrInstantiates(_)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/** Definitions for the keyboard cache query */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.security.SensitiveActions
|
||||
import semmle.code.xml.AndroidManifest
|
||||
|
||||
/** An Android Layout XML file. */
|
||||
private class AndroidLayoutXmlFile extends XmlFile {
|
||||
AndroidLayoutXmlFile() { this.getRelativePath().matches("%/res/layout/%.xml") }
|
||||
}
|
||||
|
||||
/** A component declared in an Android layout file. */
|
||||
class AndroidLayoutXmlElement extends XmlElement {
|
||||
AndroidXmlAttribute id;
|
||||
|
||||
AndroidLayoutXmlElement() {
|
||||
this.getFile() instanceof AndroidLayoutXmlFile and
|
||||
id = this.getAttribute("id")
|
||||
}
|
||||
|
||||
/** Gets the ID of this component. */
|
||||
string getId() { result = id.getValue() }
|
||||
|
||||
/** Gets the class of this component. */
|
||||
Class getClass() {
|
||||
this.getName() = "view" and
|
||||
this.getAttribute("class").getValue() = result.getQualifiedName()
|
||||
or
|
||||
this.getName() = result.getQualifiedName()
|
||||
or
|
||||
result.hasQualifiedName(["android.widget", "android.view"], this.getName())
|
||||
}
|
||||
}
|
||||
|
||||
/** An XML element that represents an editable text field. */
|
||||
class AndroidEditableXmlElement extends AndroidLayoutXmlElement {
|
||||
AndroidEditableXmlElement() {
|
||||
this.getClass().getASourceSupertype*().hasQualifiedName("android.widget", "EditText")
|
||||
}
|
||||
|
||||
/** Gets the input type of this field, if any. */
|
||||
string getInputType() { result = this.getAttribute("inputType").(AndroidXmlAttribute).getValue() }
|
||||
}
|
||||
|
||||
/** A `findViewById` or `requireViewById` method on `Activity` or `View`. */
|
||||
private class FindViewMethod extends Method {
|
||||
FindViewMethod() {
|
||||
this.hasQualifiedName("android.view", "View", ["findViewById", "requireViewById"])
|
||||
or
|
||||
exists(Method m |
|
||||
m.hasQualifiedName("android.app", "Activity", ["findViewById", "requireViewById"]) and
|
||||
this = m.getAnOverride*()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a use of the view that has the given id. */
|
||||
private MethodAccess getAUseOfViewWithId(string id) {
|
||||
exists(string name, NestedClass r_id, Field id_field |
|
||||
id = "@+id/" + name and
|
||||
result.getMethod() instanceof FindViewMethod and
|
||||
r_id.getEnclosingType().hasName("R") and
|
||||
r_id.hasName("id") and
|
||||
id_field.getDeclaringType() = r_id and
|
||||
id_field.hasName(name)
|
||||
|
|
||||
DataFlow::localExprFlow(id_field.getAnAccess(), result.getArgument(0))
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the argument of a use of `setInputType` called on the view with the given id. */
|
||||
private Argument setInputTypeForId(string id) {
|
||||
exists(MethodAccess setInputType |
|
||||
setInputType.getMethod().hasQualifiedName("android.widget", "TextView", "setInputType") and
|
||||
DataFlow::localExprFlow(getAUseOfViewWithId(id), setInputType.getQualifier()) and
|
||||
result = setInputType.getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the given field is a constant flag indicating that an input with this type will not be cached. */
|
||||
private predicate inputTypeFieldNotCached(Field f) {
|
||||
f.getDeclaringType().hasQualifiedName("android.text", "InputType") and
|
||||
(
|
||||
not f.getName().matches("%TEXT%")
|
||||
or
|
||||
f.getName().matches("%PASSWORD%")
|
||||
or
|
||||
f.hasName("TYPE_TEXT_FLAG_NO_SUGGESTIONS")
|
||||
)
|
||||
}
|
||||
|
||||
/** Configuration that finds uses of `setInputType` for non cached fields. */
|
||||
private class GoodInputTypeConf extends DataFlow::Configuration {
|
||||
GoodInputTypeConf() { this = "GoodInputTypeConf" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) {
|
||||
inputTypeFieldNotCached(node.asExpr().(FieldAccess).getField())
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node) { node.asExpr() = setInputTypeForId(_) }
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(OrBitwiseExpr bitOr |
|
||||
node1.asExpr() = bitOr.getAChildExpr() and
|
||||
node2.asExpr() = bitOr
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets a regex indicating that an input field may contain sensitive data. */
|
||||
private string getInputSensitiveInfoRegex() {
|
||||
result =
|
||||
[
|
||||
getCommonSensitiveInfoRegex(),
|
||||
"(?i).*(bank|credit|debit|(pass(wd|word|code|phrase))|security).*"
|
||||
]
|
||||
}
|
||||
|
||||
/** Holds if input using the given input type (as written in XML) is not stored in the keyboard cache. */
|
||||
bindingset[ty]
|
||||
private predicate inputTypeNotCached(string ty) {
|
||||
not ty.matches("%text%")
|
||||
or
|
||||
ty.regexpMatch("(?i).*(nosuggestions|password).*")
|
||||
}
|
||||
|
||||
/** Gets an input field whose contents may be sensitive and may be stored in the keyboard cache. */
|
||||
AndroidEditableXmlElement getASensitiveCachedInput() {
|
||||
result.getId().regexpMatch(getInputSensitiveInfoRegex()) and
|
||||
(
|
||||
not inputTypeNotCached(result.getInputType()) and
|
||||
not exists(GoodInputTypeConf conf, DataFlow::Node src, DataFlow::Node sink |
|
||||
conf.hasFlow(src, sink) and
|
||||
sink.asExpr() = setInputTypeForId(result.getId())
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import java
|
||||
|
||||
from RefType t
|
||||
where
|
||||
t.fromSource() and
|
||||
t.getFile().isJavaSourceFile() and
|
||||
not t instanceof AnonymousClass and
|
||||
not t.getName().substring(0, 1).toUpperCase() = t.getName().substring(0, 1)
|
||||
select t, "Class and interface names should start in uppercase."
|
||||
|
||||
@@ -37,6 +37,7 @@ predicate oneLineStatement(Stmt s, File f, int line, int col) {
|
||||
from Stmt s, Stmt s2
|
||||
where
|
||||
exists(File f, int line, int col, int col2 |
|
||||
f.isJavaSourceFile() and
|
||||
oneLineStatement(s, f, line, col) and
|
||||
oneLineStatement(s2, f, line, col2) and
|
||||
col < col2 and
|
||||
|
||||
@@ -30,6 +30,8 @@ where
|
||||
t2.getName().toLowerCase().matches("%visitor%") or
|
||||
t1.getAMethod().getName().toLowerCase().matches("%visit%") or
|
||||
t2.getAMethod().getName().toLowerCase().matches("%visit%") or
|
||||
t1.getPackage() = t2.getPackage()
|
||||
t1.getPackage() = t2.getPackage() or
|
||||
t1.getFile().isKotlinSourceFile() or
|
||||
t2.getFile().isKotlinSourceFile()
|
||||
)
|
||||
select t1, "This type and type $@ are mutually dependent.", t2, t2.getName()
|
||||
|
||||
@@ -14,6 +14,8 @@ import semmle.code.java.deadcode.DeadCode
|
||||
|
||||
from DeadClass c, Element origin, string reason
|
||||
where
|
||||
not c.getFile().isKotlinSourceFile() and
|
||||
not origin.getFile().isKotlinSourceFile() and
|
||||
if exists(DeadRoot root | root = c.getADeadRoot() | not root = c.getACallable())
|
||||
then (
|
||||
// Report a list of the dead roots.
|
||||
|
||||
@@ -15,6 +15,8 @@ import semmle.code.java.deadcode.DeadCode
|
||||
|
||||
from DeadField f, Element origin, string reason
|
||||
where
|
||||
not f.getFile().isKotlinSourceFile() and
|
||||
not origin.getFile().isKotlinSourceFile() and
|
||||
not f.isInDeadScope() and
|
||||
if f.getAnAccess() instanceof FieldRead
|
||||
then (
|
||||
|
||||
@@ -15,6 +15,8 @@ import semmle.code.java.deadcode.DeadCode
|
||||
|
||||
from DeadMethod c, Callable origin, string reason
|
||||
where
|
||||
not c.getFile().isKotlinSourceFile() and
|
||||
not origin.getFile().isKotlinSourceFile() and
|
||||
not c.isInDeadScope() and
|
||||
if exists(DeadRoot deadRoot | deadRoot = getADeadRoot(c) | deadRoot.getSourceDeclaration() != c)
|
||||
then (
|
||||
|
||||
@@ -64,6 +64,7 @@ class UnimplementedEquals extends EqualsMethod {
|
||||
|
||||
from EqualsMethod m
|
||||
where
|
||||
m.getFile().isJavaSourceFile() and
|
||||
exists(m.getBody()) and
|
||||
exists(Parameter p | p = m.getAParameter() |
|
||||
// The parameter has no type test
|
||||
|
||||
@@ -33,6 +33,7 @@ predicate safeReaderType(RefType t) {
|
||||
|
||||
from ClassInstanceExpr cie, RefType t
|
||||
where
|
||||
cie.getFile().isJavaSourceFile() and
|
||||
badCloseableInit(cie) and
|
||||
cie.getType() = t and
|
||||
readerType(t) and
|
||||
|
||||
@@ -15,6 +15,7 @@ import CloseType
|
||||
|
||||
from CloseableInitExpr cie, RefType t
|
||||
where
|
||||
cie.getFile().isJavaSourceFile() and
|
||||
badCloseableInit(cie) and
|
||||
cie.getType() = t and
|
||||
sqlType(t) and
|
||||
|
||||
@@ -29,6 +29,7 @@ predicate safeWriterType(RefType t) {
|
||||
|
||||
from ClassInstanceExpr cie, RefType t
|
||||
where
|
||||
cie.getFile().isJavaSourceFile() and
|
||||
badCloseableInit(cie) and
|
||||
cie.getType() = t and
|
||||
writerType(t) and
|
||||
|
||||
@@ -90,7 +90,7 @@ predicate exceptions(Class c, Field f) {
|
||||
|
||||
from Class c, Field f, string reason
|
||||
where
|
||||
c.fromSource() and
|
||||
c.getFile().isJavaSourceFile() and
|
||||
c.getAStrictAncestor() instanceof TypeSerializable and
|
||||
f.getDeclaringType() = c and
|
||||
not exceptions(c, f) and
|
||||
|
||||
@@ -77,7 +77,7 @@ predicate exceptions(NestedClass inner) {
|
||||
|
||||
from NestedClass inner, Class outer, string advice
|
||||
where
|
||||
inner.fromSource() and
|
||||
inner.getFile().isJavaSourceFile() and
|
||||
isSerializable(inner) and
|
||||
outer = enclosingInstanceType+(inner) and
|
||||
not isSerializable(outer) and
|
||||
|
||||
@@ -56,6 +56,7 @@ predicate blockParent(Stmt empty, string msg) {
|
||||
|
||||
from Stmt empty, string msg
|
||||
where
|
||||
empty.getFile().isJavaSourceFile() and
|
||||
empty = emptyBody() and
|
||||
blockParent(empty, msg)
|
||||
select empty, msg + " Typographical error or missing code?"
|
||||
|
||||
@@ -75,6 +75,7 @@ predicate isMustBeQualifierMockingMethod(Method m) {
|
||||
|
||||
predicate relevantMethodCall(MethodAccess ma, Method m) {
|
||||
// For "return value ignored", all method calls are relevant.
|
||||
not ma.getFile().isKotlinSourceFile() and
|
||||
ma.getMethod() = m and
|
||||
not m.getReturnType().hasName("void") and
|
||||
(not isMockingMethod(m) or isMustBeQualifierMockingMethod(m)) and
|
||||
|
||||
15
java/ql/src/Security/CWE/CWE-524/Example.xml
Normal file
15
java/ql/src/Security/CWE/CWE-524/Example.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<!-- BAD: This password field uses the `text` input type, which allows the input to be saved to the keyboard cache. -->
|
||||
<EditText
|
||||
android:id="@+id/password_bad"
|
||||
android:inputType="text"/>
|
||||
|
||||
<!-- GOOD: This password field uses the `textPassword` input type, which ensures that the input is not saved to the keyboard cache. -->
|
||||
<EditText
|
||||
android:id="@+id/password_good"
|
||||
android:inputType="textPassword"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>When a user enters information in a text input field on an Android application, their input is saved to a keyboard cache which provides autocomplete suggestions and predictions. There is a risk that sensitive user data, such as passwords or banking information, may be leaked to other applications via the keyboard cache.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>For input fields expected to accept sensitive information, use input types such as <code>"textNoSuggestions"</code> (or <code>"textPassword"</code> for a password) to ensure the input does not get stored in the keyboard cache.</p>
|
||||
<p>Optionally, instead of declaring an input type through XML, you can set the input type in your code using <code>TextView.setInputType()</code>.</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>In the following example, the field labeled BAD allows the password to be saved to the keyboard cache,
|
||||
whereas the field labeled GOOD uses the <code>"textPassword"</code> input type to ensure the password is not cached.</p>
|
||||
|
||||
<sample src="Example.xml" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
OWASP Mobile Application Security Testing Guide: <a href="https://github.com/OWASP/owasp-mastg/blob/b7a93a2e5e0557cc9a12e55fc3f6675f6986bb86/Document/0x05d-Testing-Data-Storage.md#determining-whether-the-keyboard-cache-is-disabled-for-text-input-fields-mstg-storage-5">Determining Whether the Keyboard Cache Is Disabled for Text Input Fields</a>.
|
||||
</li>
|
||||
<li>
|
||||
Android Developers: <a href="https://developer.android.com/reference/android/widget/TextView#attr_android:inputType">android:inputType attribute documentation.</a>
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
18
java/ql/src/Security/CWE/CWE-524/SensitiveKeyboardCache.ql
Normal file
18
java/ql/src/Security/CWE/CWE-524/SensitiveKeyboardCache.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* @name Android sensitive keyboard cache
|
||||
* @description Allowing the keyboard to cache sensitive information may result in information leaks to other applications.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 8.1
|
||||
* @id java/android/sensitive-keyboard-cache
|
||||
* @tags security
|
||||
* external/cwe/cwe-524
|
||||
* @precision medium
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.security.SensitiveKeyboardCacheQuery
|
||||
|
||||
from AndroidEditableXmlElement el
|
||||
where el = getASensitiveCachedInput()
|
||||
select el, "This input field may contain sensitive information that is saved to the keyboard cache."
|
||||
@@ -92,13 +92,16 @@ class ComparisonOrEquality extends BinaryExpr {
|
||||
|
||||
from Expr e, string pattern, string rewrite
|
||||
where
|
||||
e.(BoolCompare).simplify(pattern, rewrite)
|
||||
or
|
||||
conditionalWithBool(e, pattern, rewrite)
|
||||
or
|
||||
e.(LogNotExpr).getExpr().(ComparisonOrEquality).negate(pattern, rewrite)
|
||||
or
|
||||
e.(LogNotExpr).getExpr() instanceof LogNotExpr and
|
||||
pattern = "!!A" and
|
||||
rewrite = "A"
|
||||
e.getFile().isJavaSourceFile() and
|
||||
(
|
||||
e.(BoolCompare).simplify(pattern, rewrite)
|
||||
or
|
||||
conditionalWithBool(e, pattern, rewrite)
|
||||
or
|
||||
e.(LogNotExpr).getExpr().(ComparisonOrEquality).negate(pattern, rewrite)
|
||||
or
|
||||
e.(LogNotExpr).getExpr() instanceof LogNotExpr and
|
||||
pattern = "!!A" and
|
||||
rewrite = "A"
|
||||
)
|
||||
select e, "Expressions of the form \"" + pattern + "\" can be simplified to \"" + rewrite + "\"."
|
||||
|
||||
@@ -72,12 +72,16 @@ predicate rebox(Assignment e, Variable v) {
|
||||
|
||||
from Expr e, string conv
|
||||
where
|
||||
boxed(e) and conv = "This expression is implicitly boxed."
|
||||
or
|
||||
unboxed(e) and conv = "This expression is implicitly unboxed."
|
||||
or
|
||||
exists(Variable v | rebox(e, v) |
|
||||
conv =
|
||||
"This expression implicitly unboxes, updates, and reboxes the value of '" + v.getName() + "'."
|
||||
e.getFile().isJavaSourceFile() and
|
||||
(
|
||||
boxed(e) and conv = "This expression is implicitly boxed."
|
||||
or
|
||||
unboxed(e) and conv = "This expression is implicitly unboxed."
|
||||
or
|
||||
exists(Variable v | rebox(e, v) |
|
||||
conv =
|
||||
"This expression implicitly unboxes, updates, and reboxes the value of '" + v.getName() +
|
||||
"'."
|
||||
)
|
||||
)
|
||||
select e, conv
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `java/android/sensitive-keyboard-cache`, to detect instances of sensitive information possibly being saved to the keyboard cache.
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
annotation class SomeAnnotation
|
||||
|
||||
// Diagnostic Matches: Incomplete annotation: @kotlin.Metadata(%)
|
||||
// Diagnostic Matches: Unknown location for kotlin.Metadata
|
||||
|
||||
@@ -17,10 +17,8 @@ annotation class Ann(
|
||||
val p: Int,
|
||||
@get:JvmName("w") val q: Int)
|
||||
|
||||
// Diagnostic Matches: Incomplete annotation: @kotlin.Metadata(%)
|
||||
// Diagnostic Matches: Incomplete annotation: @kotlin.jvm.JvmName(name="changeY")
|
||||
// Diagnostic Matches: Incomplete annotation: @kotlin.jvm.JvmName(name="getX_prop")
|
||||
// Diagnostic Matches: Incomplete annotation: @kotlin.jvm.JvmName(name="method")
|
||||
// Diagnostic Matches: Incomplete annotation: @kotlin.jvm.JvmName(name="y")
|
||||
// Diagnostic Matches: Unknown location for kotlin.Metadata
|
||||
// Diagnostic Matches: Unknown location for kotlin.jvm.JvmName
|
||||
|
||||
@@ -111,7 +111,5 @@ public class TakesArrayList {
|
||||
|
||||
}
|
||||
|
||||
// Diagnostic Matches: Incomplete annotation: @kotlin.Metadata(%)
|
||||
// Diagnostic Matches: Unknown location for kotlin.Metadata
|
||||
// Diagnostic Matches: Completion failure for type: org.jetbrains.annotations.NotNull
|
||||
// Diagnostic Matches: Unknown location for org.jetbrains.annotations.NotNull
|
||||
|
||||
@@ -32,6 +32,4 @@ fun foo() {
|
||||
}
|
||||
|
||||
// Diagnostic Matches: Completion failure for type: org.jetbrains.annotations.NotNull
|
||||
// Diagnostic Matches: Incomplete annotation: @kotlin.Metadata(%)
|
||||
// Diagnostic Matches: Unknown location for kotlin.Metadata
|
||||
// Diagnostic Matches: Unknown location for org.jetbrains.annotations.NotNull
|
||||
|
||||
@@ -10,5 +10,3 @@ class B<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostic Matches: Incomplete annotation: @kotlin.Metadata(%)
|
||||
// Diagnostic Matches: Unknown location for kotlin.Metadata
|
||||
|
||||
@@ -4,5 +4,3 @@ public class Test() {
|
||||
|
||||
}
|
||||
|
||||
// Diagnostic Matches: Incomplete annotation: @kotlin.Metadata(%)
|
||||
// Diagnostic Matches: Unknown location for kotlin.Metadata
|
||||
|
||||
@@ -11,5 +11,3 @@ public class Test {
|
||||
|
||||
}
|
||||
|
||||
// Diagnostic Matches: Incomplete annotation: @kotlin.Metadata(%)
|
||||
// Diagnostic Matches: Unknown location for kotlin.Metadata
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user