diff --git a/powershell/ql/lib/semmle/code/powershell/Command.qll b/powershell/ql/lib/semmle/code/powershell/Command.qll index 76528163d420..c8fd5f14859c 100644 --- a/powershell/ql/lib/semmle/code/powershell/Command.qll +++ b/powershell/ql/lib/semmle/code/powershell/Command.qll @@ -1,4 +1,5 @@ import powershell + private predicate parseCommandName(Cmd cmd, string namespace, string name) { exists(string qualified | command(cmd, qualified, _, _, _) | namespace = qualified.regexpCapture("([^\\\\]+)\\\\([^\\\\]+)", 1) and @@ -11,6 +12,7 @@ private predicate parseCommandName(Cmd cmd, string namespace, string name) { ) } +/** A call to a command. */ class Cmd extends @command, CmdBase { override string toString() { result = "call to " + this.getQualifiedCommandName() } @@ -88,3 +90,8 @@ class Cmd extends @command, CmdBase { Redirection getARedirection() { result = this.getRedirection(_) } } + +/** A call to operator `&`. */ +class CallOperator extends Cmd { + CallOperator() { this.getKind() = 28 } +} diff --git a/powershell/ql/lib/semmle/code/powershell/Variable.qll b/powershell/ql/lib/semmle/code/powershell/Variable.qll index 39ac5bdabb2a..f5cb7259e0e5 100644 --- a/powershell/ql/lib/semmle/code/powershell/Variable.qll +++ b/powershell/ql/lib/semmle/code/powershell/Variable.qll @@ -273,6 +273,21 @@ class LocalVariable extends AbstractLocalScopeVariable, TLocalVariable { final override Scope getDeclaringScope() { result = scope } } +/** + * A variable of the form `$Env:HOME`. + */ +class EnvVariable extends Variable { + string var; + + EnvVariable() { this.getName() = ["env:", "Env:"] + var } + + /** + * Gets the part of the variable name that represens which environment + * variable. + */ + string getEnvironmentVariable() { result = var } +} + class Parameter extends AbstractLocalScopeVariable, TParameter { ParameterImpl p; diff --git a/powershell/ql/lib/semmle/code/powershell/VariableExpression.qll b/powershell/ql/lib/semmle/code/powershell/VariableExpression.qll index 4259ecd79ea7..b07774a29246 100644 --- a/powershell/ql/lib/semmle/code/powershell/VariableExpression.qll +++ b/powershell/ql/lib/semmle/code/powershell/VariableExpression.qll @@ -48,3 +48,12 @@ class VarWriteAccess extends VarAccess { predicate isImplicit() { isImplicitVariableWriteAccess(this) } } + +/** An access to an environment variable such as `$Env:PATH` */ +class EnvVarAccess extends VarAccess { + EnvVarAccess() { super.getVariable() instanceof EnvVariable } + + override EnvVariable getVariable() { result = super.getVariable() } + + string getEnvironmentVariable() { result = this.getVariable().getEnvironmentVariable() } +} diff --git a/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll b/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll index a55fa3f680ae..27e43029dee9 100644 --- a/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll +++ b/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll @@ -597,6 +597,11 @@ module StmtNodes { final override string getName() { result = s.getCmdName().getValue().getValue() } } + /** A control-flow node that wraps a call to operator `&` */ + class CallOperatorCfgNode extends CmdCfgNode { + CallOperatorCfgNode() { this.getStmt() instanceof CallOperator } + } + private class AssignStmtChildMapping extends PipelineBaseChildMapping, AssignStmt { override predicate relevantChild(Ast n) { n = this.getLeftHandSide() or n = this.getRightHandSide() diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/flowsources/FlowSources.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/flowsources/FlowSources.qll index 71f9450e8338..dc76e8bedd60 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/flowsources/FlowSources.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/flowsources/FlowSources.qll @@ -1,5 +1,5 @@ /** Provides classes representing various flow sources for taint tracking. */ -import semmle.code.powershell.dataflow.internal.DataFlowPublic as DataFlow +private import semmle.code.powershell.dataflow.internal.DataFlowPublic as DataFlow import semmle.code.powershell.dataflow.flowsources.Remote import semmle.code.powershell.dataflow.flowsources.Local import semmle.code.powershell.frameworks.data.internal.ApiGraphModels diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/flowsources/Local.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/flowsources/Local.qll index 48f64b17be4b..b27d24a051c6 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/flowsources/Local.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/flowsources/Local.qll @@ -30,6 +30,12 @@ abstract class EnvironmentVariableSource extends LocalFlowSource { override string getSourceType() { result = "environment variable" } } +private class EnvironmentVariableEnv extends EnvironmentVariableSource { + EnvironmentVariableEnv() { + this.asExpr().getExpr().(VarReadAccess).getVariable() instanceof EnvVariable + } +} + private class ExternalEnvironmentVariableSource extends EnvironmentVariableSource { ExternalEnvironmentVariableSource() { this = ModelOutput::getASourceNode("environment", _).asSource() diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll index 8394a7045589..63e3eaab755b 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/DataFlowPublic.qll @@ -448,9 +448,30 @@ class CallNode extends AstNode { Node getQualifier() { result.asExpr() = call.getQualifier() } + /** Gets the i'th argument to this call. */ + Node getArgument(int i) { result.asExpr() = call.getArgument(i) } + + /** Gets the i'th positional argument to this call. */ + Node getPositionalArgument(int i) { result.asExpr() = call.getPositionalArgument(i) } + + /** Gets the argument with the name `name`, if any. */ + Node getNamedArgument(string name) { result.asExpr() = call.getNamedArgument(name) } + + /** + * Gets any argument of this call. + * + * Note that this predicate doesn't get the pipeline argument, if any. + */ + Node getAnArgument() { result.asExpr() = call.getAnArgument() } + int getNumberOfArguments() { result = call.getNumberOfArguments() } } +/** A call to operator `&`, viwed as a node in a data flow graph. */ +class CallOperatorNode extends CallNode { + CallOperatorNode() { this.getCallNode() instanceof CfgNodes::StmtNodes::CallOperatorCfgNode } +} + /** A use of a type name, viewed as a node in a data flow graph. */ class TypeNameNode extends ExprNode { override CfgNodes::ExprNodes::TypeNameCfgNode n; diff --git a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/TaintTrackingPrivate.qll b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/TaintTrackingPrivate.qll index 33b40ea7f962..094379b04cda 100644 --- a/powershell/ql/lib/semmle/code/powershell/dataflow/internal/TaintTrackingPrivate.qll +++ b/powershell/ql/lib/semmle/code/powershell/dataflow/internal/TaintTrackingPrivate.qll @@ -65,3 +65,15 @@ private module Cached { } import Cached +import SpeculativeTaintFlow + +private module SpeculativeTaintFlow { + private import semmle.code.powershell.dataflow.internal.DataFlowDispatch as DataFlowDispatch + private import semmle.code.powershell.dataflow.internal.DataFlowPublic as DataFlowPublic + + /** + * Holds if the additional step from `src` to `sink` should be considered in + * speculative taint flow exploration. + */ + predicate speculativeTaintStep(DataFlow::Node src, DataFlow::Node sink) { none() } +}