From 22e508b85bece3c7952b06f5aaa80710db442770 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 4 Oct 2024 19:13:51 +0100 Subject: [PATCH] PS: Introduce a class for constant values. --- .../code/powershell/ConstantExpression.qll | 116 ++++++++++++++++++ .../lib/semmle/code/powershell/Expression.qll | 10 +- .../code/powershell/controlflow/CfgNodes.qll | 2 + 3 files changed, 127 insertions(+), 1 deletion(-) diff --git a/powershell/ql/lib/semmle/code/powershell/ConstantExpression.qll b/powershell/ql/lib/semmle/code/powershell/ConstantExpression.qll index 9d0e6f23980..11c9b3a4caf 100644 --- a/powershell/ql/lib/semmle/code/powershell/ConstantExpression.qll +++ b/powershell/ql/lib/semmle/code/powershell/ConstantExpression.qll @@ -9,3 +9,119 @@ class ConstExpr extends @constant_expression, BaseConstExpr { override string toString() { result = this.getValue().toString() } } + +private newtype TConstantValue = + TConstInteger(int value) { + exists(ConstExpr ce | ce.getType() = "Int32" and ce.getValue().getValue().toInt() = value) + } or + TConstDouble(float double) { + exists(ConstExpr ce | ce.getType() = "Double" and ce.getValue().getValue().toFloat() = double) + } or + TConstString(string value) { exists(StringLiteral sl | sl.getValue() = value) } or + TConstBoolean(boolean value) { + exists(VarAccess va | + value = true and + va.getUserPath() = "true" + or + value = false and + va.getUserPath() = "false" + ) + } or + TNull() + +/** A constant value. */ +class ConstantValue extends TConstantValue { + /** Gets a string representation of this value. */ + final string toString() { result = this.getValue() } + + /** Gets the value of this consant. */ + string getValue() { none() } + + /** Gets the integer value of this constant, if any. */ + int asInt() { none() } + + /** Gets the floating point value of this constant, if any. */ + float asDouble() { none() } + + /** Gets the string value of this constant, if any. */ + string asString() { none() } + + /** Gets the boolean value of this constant, if any. */ + boolean asBoolean() { none() } + + /** Holds if this constant represents the null value. */ + predicate isNull() { none() } + + /** Gets a (unique) serialized version of this value. */ + string serialize() { none() } + + /** Gets an exprssion that has this value. */ + Expr getAnExpr() { none() } +} + +/** A constant integer value */ +class ConstInteger extends ConstantValue, TConstInteger { + final override int asInt() { this = TConstInteger(result) } + + final override string getValue() { result = this.asInt().toString() } + + final override string serialize() { result = this.getValue() } + + final override ConstExpr getAnExpr() { result.getValue().getValue() = this.getValue() } +} + +/** A constant floating point value. */ +class ConstDouble extends ConstantValue, TConstDouble { + final override float asDouble() { this = TConstDouble(result) } + + final override string getValue() { result = this.asDouble().toString() } + + final override string serialize() { + exists(string res | res = this.asDouble().toString() | + if exists(res.indexOf(".")) then result = res else result = res + ".0" + ) + } + + final override ConstExpr getAnExpr() { result.getValue().getValue() = this.getValue() } +} + +/** A constant string value. */ +class ConstString extends ConstantValue, TConstString { + final override string asString() { this = TConstString(result) } + + final override string getValue() { result = this.asString() } + + final override string serialize() { + result = "\"" + this.asString().replaceAll("\"", "\\\"") + "\"" + } + + final override ConstExpr getAnExpr() { result.getValue().getValue() = this.getValue() } +} + +/** A constant boolean value. */ +class ConstBoolean extends ConstantValue, TConstBoolean { + final override boolean asBoolean() { this = TConstBoolean(result) } + + final override string getValue() { result = this.asBoolean().toString() } + + final override string serialize() { result = this.getValue() } + + final override VarAccess getAnExpr() { + this.asBoolean() = true and + result.getUserPath() = "true" + or + this.asBoolean() = false and + result.getUserPath() = "false" + } +} + +/** The constant null value. */ +class NullConst extends ConstantValue, TNull { + final override predicate isNull() { any() } + + final override string getValue() { result = "null" } + + final override string serialize() { result = this.getValue() } + + final override VarAccess getAnExpr() { result.getUserPath() = "null" } +} diff --git a/powershell/ql/lib/semmle/code/powershell/Expression.qll b/powershell/ql/lib/semmle/code/powershell/Expression.qll index 97df75538c0..909c971f6e6 100644 --- a/powershell/ql/lib/semmle/code/powershell/Expression.qll +++ b/powershell/ql/lib/semmle/code/powershell/Expression.qll @@ -1,3 +1,11 @@ import powershell -class Expr extends @expression, CmdElement { } +/** + * An expression. + * + * This is the topmost class in the hierachy of all expression in PowerShell. + */ +class Expr extends @expression, CmdElement { + /** Gets the constant value of this expression, if this is known. */ + final ConstantValue getValue() { result.getAnExpr() = this } +} diff --git a/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll b/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll index 642c03c5414..ad8c3909bed 100644 --- a/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll +++ b/powershell/ql/lib/semmle/code/powershell/controlflow/CfgNodes.qll @@ -46,6 +46,8 @@ class ExprCfgNode extends AstCfgNode { /** Gets the underlying expression. */ Expr getExpr() { result = e } + + final ConstantValue getValue() { result = e.getValue() } } /** A control-flow node that wraps an AST statement. */