Merge branch 'main' into optionals2

This commit is contained in:
Geoffrey White
2022-11-17 18:16:46 +00:00
208 changed files with 16282 additions and 3068 deletions

View File

@@ -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

View File

@@ -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>

View File

@@ -17,7 +17,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build" Version="16.11.0" />
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
</ItemGroup>
<ItemGroup>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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() + ")"
}
/*

View File

@@ -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() }
}

View File

@@ -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

View File

@@ -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) |

View File

@@ -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, _)
}

View File

@@ -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);

View File

@@ -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)

View File

@@ -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)`.

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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))) }
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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(), _)
)
}

View File

@@ -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()

View File

@@ -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`.
*

View File

@@ -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

View File

@@ -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) }

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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)
)
}
}

View File

@@ -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 }
}

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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.",

View File

@@ -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())
)

View File

@@ -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?

View File

@@ -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 |

View File

@@ -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

View File

@@ -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

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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

View File

@@ -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 |

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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 | |

View File

@@ -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

View File

@@ -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

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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

View File

@@ -274,7 +274,7 @@ class DeadMethod extends Callable {
class RootdefCallable extends Callable {
RootdefCallable() {
this.fromSource() and
this.getFile().isJavaSourceFile() and
not this.(Method).overridesOrInstantiates(_)
}

View File

@@ -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())
)
)
}

View File

@@ -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."

View File

@@ -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

View File

@@ -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()

View File

@@ -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.

View File

@@ -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 (

View File

@@ -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 (

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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?"

View File

@@ -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

View 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>

View File

@@ -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>

View 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."

View File

@@ -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 + "\"."

View File

@@ -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

View File

@@ -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.

View File

@@ -1,5 +1,3 @@
annotation class SomeAnnotation
// Diagnostic Matches: Incomplete annotation: @kotlin.Metadata(%)
// Diagnostic Matches: Unknown location for kotlin.Metadata

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -10,5 +10,3 @@ class B<T> {
}
}
// Diagnostic Matches: Incomplete annotation: @kotlin.Metadata(%)
// Diagnostic Matches: Unknown location for kotlin.Metadata

View File

@@ -4,5 +4,3 @@ public class Test() {
}
// Diagnostic Matches: Incomplete annotation: @kotlin.Metadata(%)
// Diagnostic Matches: Unknown location for kotlin.Metadata

View File

@@ -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