Skip to content

PS: Global flow through environment variables #261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,16 @@ newtype ChildIndex =
ExprRedirection(int i) { exists(any(Raw::Cmd cmdExpr).getRedirection(i)) } or
FunDefFun() or
TypeDefType() or
TypeMember(int i) {
exists(any(Raw::TypeStmt typedef).getMember(i))
// or
// hasMemberInType(_, _, i, _)
} or
TypeMember(int i) { exists(any(Raw::TypeStmt typedef).getMember(i)) } or
ThisVar() or
PipelineIteratorVar() or
PipelineByPropertyNameIteratorVar(Raw::PipelineByPropertyNameParameter p) or
RealVar(string name) { name = variableNameInScope(_, _) } or
ProcessBlockPipelineVarReadAccess() or
ProcessBlockPipelineByPropertyNameVarReadAccess(string name) {
name = any(Raw::PipelineByPropertyNameParameter p).getLowerCaseName()
}
} or
EnvVar(string var) { Raw::isEnvVariableAccess(_, var) }

int synthPipelineParameterChildIndex(Raw::ScriptBlock sb) {
// If there is a parameter block, but no pipeline parameter
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
private import AstImport

class EnvVariable extends Expr, TEnvVariable {
final override string toString() { result = this.getName() }
class EnvVariable extends Variable instanceof EnvVariableImpl {
string getLowerCaseName() { result = super.getLowerCaseNameImpl() }

string getName() { any(Synthesis s).envVariableName(this, result) }
}
bindingset[name]
pragma[inline_late]
final predicate matchesName(string name) { this.getLowerCaseName() = name.toLowerCase() }

bindingset[result]
pragma[inline_late]
final string getAName() { result.toLowerCase() = this.getLowerCaseName() }

class SystemDrive extends EnvVariable {
SystemDrive() { this.getName() = "systemdrive" }
}
override Ast getChild(ChildIndex childIndex) { none() }
}
90 changes: 72 additions & 18 deletions powershell/ql/lib/semmle/code/powershell/ast/internal/Synthesis.qll
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ private import Type
private import Scopes
private import BoolLiteral
private import Member
private import EnvVariable
private import Raw.Raw as Raw
private import codeql.util.Boolean
private import AutomaticVariable

newtype VarKind =
ThisVarKind() or
EnvVarKind(string var) { Raw::isEnvVariableAccess(_, var) } or
ParamVarRealKind() or
PipelineIteratorKind() or
PipelineByPropertyNameIteratorKind(string name) {
Expand All @@ -39,7 +39,6 @@ newtype SynthKind =
TypeSynthKind() or
BoolLiteralKind(Boolean b) or
NullLiteralKind() or
EnvVariableKind(string var) { Raw::isEnvVariableAccess(_, var) } or
AutomaticVariableKind(string var) { Raw::isAutomaticVariableAccess(_, var) } or
VarSynthKind(VarKind k)

Expand Down Expand Up @@ -96,8 +95,6 @@ class Synthesis extends TSynthesis {

predicate booleanValue(BoolLiteral b, boolean value) { none() }

predicate envVariableName(EnvVariable var, string name) { none() }

predicate automaticVariableName(AutomaticVariable var, string name) { none() }

final string toString() { none() }
Expand Down Expand Up @@ -152,6 +149,77 @@ private module ThisSynthesis {
}
}

private module EnvironmentVariables {
bindingset[var]
private Raw::TopLevelScriptBlock getScope(string var) {
result =
min(Raw::TopLevelScriptBlock scriptBlock, Raw::VarAccess va, Location loc |
Raw::isEnvVariableAccess(va, var) and
va.getParent+() = scriptBlock and
loc = scriptBlock.getLocation()
|
scriptBlock order by loc.getFile().getAbsolutePath()
)
}

private class EnvironmentVariables extends Synthesis {
final override predicate child(Raw::Ast parent, ChildIndex i, Child child) {
exists(Raw::VarAccess va, string s0 |
parent = getScope(s0) and
va.getUserPath().toLowerCase() = "env:" + s0 and
Raw::isEnvVariableAccess(va, s0) and
child = SynthChild(VarSynthKind(EnvVarKind(s0))) and
i = EnvVar(s0)
)
}

override predicate variableSynthName(VariableSynth v, string name) {
exists(string name0 |
v = TVariableSynth(_, EnvVar(name0)) and
name = "env:" + name0
)
}
}
}

private module EnvironmentVariableAccessSynth {
private class EnvVarAccessSynthesis extends Synthesis {
final override predicate isRelevant(Raw::Ast a) { Raw::isEnvVariableAccess(a, _) }

final override VarAccess getResultAstImpl(Raw::Ast r) {
exists(Raw::Ast parent, ChildIndex i |
this.envVarAccess(parent, i, _, r, _) and
result = TVarAccessSynth(parent, i)
)
}

private predicate envVarAccess(Raw::Ast parent, ChildIndex i, Child child, Raw::VarAccess va, string var) {
va = parent.getChild(toRawChildIndex(i)) and
Raw::isEnvVariableAccess(va, var) and
child = SynthChild(VarAccessSynthKind(TVariableSynth(_, EnvVar(var))))
}

override predicate child(Raw::Ast parent, ChildIndex i, Child child) {
this.envVarAccess(parent, i, child, _, _)
}

final override predicate getAnAccess(VarAccessSynth va, Variable v) {
exists(Raw::Ast parent, ChildIndex i, string var |
this.envVarAccess(parent, i, _, _, var) and
v = TVariableSynth(_, EnvVar(var)) and
va = TVarAccessSynth(parent, i)
)
}

override Location getLocation(Ast n) {
exists(Raw::Ast scope |
n = TVariableSynth(scope, EnvVar(_)) and
result = scope.getLocation()
)
}
}
}

private module SetVariableAssignment {
private class SetVariableAssignment extends Synthesis {
override predicate explicitAssignment(Raw::Ast dest, string name, Raw::Ast assignment) {
Expand Down Expand Up @@ -673,7 +741,6 @@ private module LiteralSynth {
exists(Raw::Ast parent, ChildIndex i | this.child(parent, i, _, r) |
result = TBoolLiteral(parent, i) or
result = TNullLiteral(parent, i) or
result = TEnvVariable(parent, i) or
result = TAutomaticVariable(parent, i)
)
}
Expand All @@ -692,12 +759,6 @@ private module LiteralSynth {
s = "null" and
child = SynthChild(NullLiteralKind())
or
exists(string s0 |
s = "env:" + s0 and
Raw::isEnvVariableAccess(va, s0) and
child = SynthChild(EnvVariableKind(s0))
)
or
isAutomaticVariableAccess(va, s) and
child = SynthChild(AutomaticVariableKind(s))
)
Expand All @@ -714,13 +775,6 @@ private module LiteralSynth {
)
}

final override predicate envVariableName(EnvVariable var, string name) {
exists(Raw::Ast parent, ChildIndex i |
var = TEnvVariable(parent, i) and
this.child(parent, i, SynthChild(EnvVariableKind(name)))
)
}

final override predicate automaticVariableName(AutomaticVariable var, string name) {
exists(Raw::Ast parent, ChildIndex i |
var = TAutomaticVariable(parent, i) and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@ private module Cached {
TUsingExpr(Raw::UsingExpr u) or
TBoolLiteral(Raw::Ast parent, ChildIndex i) { mkSynthChild(BoolLiteralKind(_), parent, i) } or
TNullLiteral(Raw::Ast parent, ChildIndex i) { mkSynthChild(NullLiteralKind(), parent, i) } or
TEnvVariable(Raw::Ast parent, ChildIndex i) { mkSynthChild(EnvVariableKind(_), parent, i) } or
TAutomaticVariable(Raw::Ast parent, ChildIndex i) {
mkSynthChild(AutomaticVariableKind(_), parent, i)
}
Expand All @@ -180,15 +179,15 @@ private module Cached {

class TAstSynth =
TExprStmtSynth or TFunctionSynth or TBoolLiteral or TNullLiteral or TVarAccessSynth or
TEnvVariable or TTypeSynth or TAutomaticVariable or TVariableSynth;
TTypeSynth or TAutomaticVariable or TVariableSynth;

class TExpr =
TArrayExpr or TArrayLiteral or TOperation or TConstExpr or TConvertExpr or TErrorExpr or
THashTableExpr or TIndexExpr or TInvokeMemberExpr or TCmd or TMemberExpr or TPipeline or
TPipelineChain or TStringConstExpr or TConditionalExpr or TVarAccess or
TExpandableStringExpr or TScriptBlockExpr or TExpandableSubExpr or TTypeNameExpr or
TUsingExpr or TAttributedExpr or TIf or TBoolLiteral or TNullLiteral or TThisExpr or
TEnvVariable or TAutomaticVariable or TParenExpr;
TAutomaticVariable or TParenExpr;

class TStmt =
TAssignStmt or TBreakStmt or TContinueStmt or TDataStmt or TDoUntilStmt or TDoWhileStmt or
Expand Down Expand Up @@ -308,7 +307,6 @@ private module Cached {
result = TBoolLiteral(parent, i) or
result = TNullLiteral(parent, i) or
result = TVarAccessSynth(parent, i) or
result = TEnvVariable(parent, i) or
result = TTypeSynth(parent, i) or
result = TAutomaticVariable(parent, i) or
result = TVariableSynth(parent, i)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ module Private {
}
}

class EnvVariableImpl extends VariableSynth {
override EnvVar i;
}

abstract class VarAccessImpl extends Expr, TVarAccess {
abstract VariableImpl getVariableImpl();
}
Expand Down
14 changes: 0 additions & 14 deletions powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1093,20 +1093,6 @@ module ExprNodes {
CallExprCfgNode getCall() { result.getPipelineArgument() = this }
}

private class EnvVariableChildMapping extends ExprChildMapping, EnvVariable {
override predicate relevantChild(Ast child) { none() }
}

class EnvVariableCfgNode extends ExprCfgNode {
override string getAPrimaryQlClass() { result = "EnvVariableCfgNode" }

override EnvVariableChildMapping e;

override EnvVariable getExpr() { result = e }

string getName() { result = e.getName() }
}

private class OperationChildMapping extends ExprChildMapping, Operation {
override predicate relevantChild(Ast child) { child = this.getAnOperand() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -736,8 +736,6 @@ module Trees {

class VarTree extends LeafTree instanceof Variable { }

class EnvVariableTree extends LeafTree instanceof EnvVariable { }

class AutomaticVariableTree extends LeafTree instanceof AutomaticVariable { }

class BinaryExprTree extends StandardPostOrderTree instanceof BinaryExpr {
Expand Down
15 changes: 15 additions & 0 deletions powershell/ql/lib/semmle/code/powershell/dataflow/Ssa.qll
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,21 @@ module Ssa {
final override Location getLocation() { result = this.getBasicBlock().getLocation() }
}

class InitialEnvVarDefinition extends Definition, SsaImpl::WriteDefinition {
private EnvVariable v;

InitialEnvVarDefinition() {
exists(BasicBlock bb, int i |
this.definesAt(v, bb, i) and
SsaImpl::envVarWrite(bb, i, v)
)
}

final override string toString() { result = "<initial env var> " + v }

final override Location getLocation() { result = this.getBasicBlock().getLocation() }
}

/** phi node. */
class PhiNode extends Definition, SsaImpl::PhiNode {
/** Gets an input of this phi node. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ abstract class EnvironmentVariableSource extends LocalFlowSource {
}

private class EnvironmentVariableEnv extends EnvironmentVariableSource {
EnvironmentVariableEnv() { this.asExpr().getExpr() instanceof EnvVariable }
EnvironmentVariableEnv() { this.asExpr().getExpr() = any(EnvVariable env).getAnAccess() }
}

private class ExternalEnvironmentVariableSource extends EnvironmentVariableSource {
Expand Down
Loading