-
Dear everybody, CodeQL Query: /**
* @name Static slicing
* @description An attempt to perform a static slice
* @id cs/static-slicing
* @problem.severity error
* @precision very-high
* @tags security
* @kind path-problem
*/
import csharp
import semmle.code.csharp.security.dataflow.flowsources.Remote
import semmle.code.csharp.commons.Util
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
import semmle.code.csharp.controlflow.Guards
import semmle.code.csharp.controlflow.BasicBlocks
import semmle.code.csharp.ExprOrStmtParent
class TaintTrackingConfiguration extends TaintTracking::Configuration {
TaintTrackingConfiguration() { this = "CustomSlice" }
override predicate isSource(DataFlow::Node source) {
any()
}
override predicate isSink(DataFlow::Node sink) {
sink.asExpr().getLocation().getFile().getAbsolutePath().indexOf("Program.cs") > 0
and sink.asExpr().getLocation().getStartLine() = 12 // Target of the slicer (catching the variable result)
}
// An attempt to add the control dependencies
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists (Guard g, BasicBlock b, AbstractValue v |
g.getAControlFlowNode() = pred.getControlFlowNode() and
b.getANode() = succ.getControlFlowNode() and
g.controlsBasicBlock(b, v)
)
}
}
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink
where c.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Msg", source.getNode(), "Msg" So I run this query to the next source code: using System;
namespace example2
{
public class Program
{
public static void Main(string[] args)
{
int levels = 5;
TreeNode root = new TreeNode(levels);
int result = root.addTree(); // *
var slicingVariable1 = result; // *
System.Console.WriteLine("Done!");
}
}
internal class TreeNode
{
private int value = 0;
private TreeNode left = null;
private TreeNode right = null;
internal TreeNode(int levels)
{
value = 1;
if (levels <= 1)
{
if (levels <= 0)
throw new System.Exception("Number of levels must be positive no.");
left = null;
right = null;
}
else
{
left = new TreeNode(levels - 1);
right = new TreeNode(levels - 1);
}
}
internal virtual int addTree()
{
int total = value; // *
if (left != null) // *
total += left.addTree(); // *
if (right != null) // *
total += right.addTree(); // *
return total; // *
}
}
} After running the query I collect every line in every path, and I get these lines: {11, 12, 42, 43, 44, 45, 46, 47} (marked with // *). For example, Any suggestions? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Hi @asoifer, I'm sorry to say that our data-flow analysis has a specific limitation that disallows defining sources/sinks of a data-flow configuration based on paths found with that configuration. For the security queries we are writing, that has not been a big problem so far. In some cases, we've had to have two layers of data-flow, which we have managed by just introducing a separate copy of the whole data-flow analysis, and then each part can sit in it's own "layer". In this way we can define sources/sinks in one layer based on the results form the other layer. For C# this would be DataFlow, DataFlow2, DataFlow3, DataFlow4, DataFlow5. And as an example of actually combining such two layers, I will highlight path injection query in Python. But I think this would at best be a band-aid for the specific problem you are trying to solve, where I would assume you want to reach a fixed point, and not just do a few iterations at most. Also sorry for the very late reply. This question somehow slipped off the radar 😐 |
Beta Was this translation helpful? Give feedback.
Hi @asoifer,
I'm sorry to say that our data-flow analysis has a specific limitation that disallows defining sources/sinks of a data-flow configuration based on paths found with that configuration. For the security queries we are writing, that has not been a big problem so far. In some cases, we've had to have two layers of data-flow, which we have managed by just introducing a separate copy of the whole data-flow analysis, and then each part can sit in it's own "layer". In this way we can define sources/sinks in one layer based on the results form the other layer.
For C# this would be DataFlow, DataFlow2, DataFlow3, DataFlow4, DataFlow5. And as an example of actually combining such two la…