Merge pull request #7985 from github/nickrolfe/constant_regexp

Ruby: separate constant propagation of regexps from strings
This commit is contained in:
Nick Rolfe
2022-03-30 11:37:33 +01:00
committed by GitHub
30 changed files with 1952 additions and 1843 deletions

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* Whereas `ConstantValue::getString()` previously returned both string and regular-expression values, it now returns only string values. The same applies to `ConstantValue::isString(value)`.
* Regular-expression values can now be accessed with the new predicates `ConstantValue::getRegExp()`, `ConstantValue::isRegExp(value)`, and `ConstantValue::isRegExpWithFlags(value, flags)`.

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* `ConstantValue::getStringOrSymbol` and `ConstantValue::isStringOrSymbol`, which return/hold for all string-like values (strings, symbols, and regular expressions), have been renamed to `ConstantValue::getStringlikeValue` and `ConstantValue::isStringlikeValue`, respectively. The old names have been marked as `deprecated`.

View File

@@ -268,7 +268,7 @@ module HTTP {
string getUrlPattern() {
exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
this.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(strNode) and
result = strNode.getExpr().getConstantValue().getStringOrSymbol()
result = strNode.getExpr().getConstantValue().getStringlikeValue()
)
}
@@ -431,7 +431,7 @@ module HTTP {
string getMimetype() {
exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
this.getMimetypeOrContentTypeArg().getALocalSource() = DataFlow::exprNode(strNode) and
result = strNode.getExpr().getConstantValue().getStringOrSymbol().splitAt(";", 0)
result = strNode.getExpr().getConstantValue().getStringlikeValue().splitAt(";", 0)
)
or
not exists(this.getMimetypeOrContentTypeArg()) and

View File

@@ -8,28 +8,37 @@ private import internal.TreeSitter
/** A constant value. */
class ConstantValue extends TConstantValue {
/** Gets a textual representation of this constant value. */
final string toString() {
result = this.getInt().toString()
final string toString() { this.hasValueWithType(result, _) }
/** Gets a string describing the type of this constant value. */
string getValueType() { this.hasValueWithType(_, result) }
private predicate hasValueWithType(string value, string type) {
value = this.getInt().toString() and type = "int"
or
result = this.getFloat().toString()
value = this.getFloat().toString() and type = "float"
or
exists(int numerator, int denominator |
this.isRational(numerator, denominator) and
result = numerator + "/" + denominator
value = numerator + "/" + denominator and
type = "rational"
)
or
exists(float real, float imaginary |
this.isComplex(real, imaginary) and
result = real + "+" + imaginary + "i"
value = real + "+" + imaginary + "i" and
type = "complex"
)
or
result = this.getString()
value = this.getString() and type = "string"
or
result = ":" + this.getSymbol()
value = ":" + this.getSymbol() and type = "symbol"
or
result = this.getBoolean().toString()
value = this.getRegExp() and type = "regexp"
or
this.isNil() and result = "nil"
value = this.getBoolean().toString() and type = "boolean"
or
this.isNil() and value = "nil" and type = "nil"
}
/** Gets the integer value, if this is an integer. */
@@ -62,11 +71,31 @@ class ConstantValue extends TConstantValue {
/** Holds if this is the symbol value `:s`. */
predicate isSymbol(string s) { s = this.getSymbol() }
/** Gets the string or symbol value, if any. */
string getStringOrSymbol() { result = [this.getString(), this.getSymbol()] }
/** Gets the regexp value, if this is a regexp. */
string getRegExp() { this.isRegExpWithFlags(result, _) }
/** Holds if this is the string value `s` or the symbol value `:s`. */
predicate isStringOrSymbol(string s) { s = this.getStringOrSymbol() }
/** Holds if this is the regexp value `/s/`, ignoring any flags. */
predicate isRegExp(string s) { this.isRegExpWithFlags(s, _) }
/** Holds if this is the regexp value `/s/flags` . */
predicate isRegExpWithFlags(string s, string flags) { this = TRegExp(s, flags) }
/** DEPRECATED: Use `getStringlikeValue` instead. */
deprecated string getStringOrSymbol() { result = this.getStringlikeValue() }
/** DEPRECATED: Use `isStringlikeValue` instead. */
deprecated predicate isStringOrSymbol(string s) { s = this.getStringlikeValue() }
/** Gets the string/symbol/regexp value, if any. */
string getStringlikeValue() { result = [this.getString(), this.getSymbol(), this.getRegExp()] }
/**
* Holds if this is:
* - the string value `s`,
* - the symbol value `:s`, or
* - the regexp value `/s/`.
*/
predicate isStringlikeValue(string s) { s = this.getStringlikeValue() }
/** Gets the Boolean value, if this is a Boolean. */
boolean getBoolean() { this = TBoolean(result) }
@@ -92,11 +121,17 @@ module ConstantValue {
/** A constant complex value. */
class ConstantComplexValue extends ConstantValue, TComplex { }
/** A constant string-like value. */
class ConstantStringlikeValue extends ConstantValue, TStringlike { }
/** A constant string value. */
class ConstantStringValue extends ConstantValue, TString { }
class ConstantStringValue extends ConstantStringlikeValue, TString { }
/** A constant symbol value. */
class ConstantSymbolValue extends ConstantValue, TSymbol { }
class ConstantSymbolValue extends ConstantStringlikeValue, TSymbol { }
/** A constant regexp value. */
class ConstantRegExpValue extends ConstantStringlikeValue, TRegExp { }
/** A constant Boolean value. */
class ConstantBooleanValue extends ConstantValue, TBoolean { }

View File

@@ -454,11 +454,11 @@ class StringConcatenation extends Expr, TStringConcatenation {
*/
final string getConcatenatedValueText() {
forall(StringLiteral c | c = this.getString(_) |
exists(c.getConstantValue().getStringOrSymbol())
exists(c.getConstantValue().getStringlikeValue())
) and
result =
concat(string valueText, int i |
valueText = this.getString(i).getConstantValue().getStringOrSymbol()
valueText = this.getString(i).getConstantValue().getStringlikeValue()
|
valueText order by i
)

View File

@@ -53,7 +53,7 @@ private class MethodModifier extends MethodCall {
predicate modifiesMethod(Namespace n, string name) {
this = n.getAStmt() and
[
this.getMethodArgument().getConstantValue().getStringOrSymbol(),
this.getMethodArgument().getConstantValue().getStringlikeValue(),
this.getMethodArgument().(MethodBase).getName()
] = name
}

View File

@@ -278,7 +278,7 @@ class HashPattern extends CasePattern, THashPattern {
/** Gets the value for a given key name. */
CasePattern getValueByKey(string key) {
exists(int i |
this.getKey(i).getConstantValue().isStringOrSymbol(key) and result = this.getValue(i)
this.getKey(i).getConstantValue().isStringlikeValue(key) and result = this.getValue(i)
)
}

View File

@@ -229,9 +229,16 @@ private module Propagation {
}
pragma[nomagic]
string getNonSymbolValue() {
string getStringValue() {
result = this.getValue() and
not this.getExpr() instanceof SymbolLiteral
not this.getExpr() instanceof SymbolLiteral and
not this.getExpr() instanceof RegExpLiteral
}
pragma[nomagic]
string getRegExpValue(string flags) {
result = this.getValue() and
flags = this.getExpr().(RegExpLiteral).getFlagString()
}
}
@@ -251,7 +258,7 @@ private module Propagation {
s = left + right
)
or
s = e.(StringlikeLiteralWithInterpolationCfgNode).getNonSymbolValue()
s = e.(StringlikeLiteralWithInterpolationCfgNode).getStringValue()
or
// If last statement in the interpolation is a constant or local variable read,
// we attempt to look up its string value.
@@ -267,13 +274,15 @@ private module Propagation {
exists(ExprCfgNode last | last = e.(RegExpInterpolationComponentCfgNode).getLastStmt() |
isInt(last, any(int i | s = i.toString())) or
isFloat(last, any(float f | s = f.toString())) or
isString(last, s)
isString(last, s) or
isRegExp(last, s, _) // Note: we lose the flags for interpolated regexps here.
)
}
private predicate isStringExprNoCfg(Expr e, string s) {
s = e.(StringlikeLiteralImpl).getStringValue() and
not e instanceof SymbolLiteral
not e instanceof SymbolLiteral and
not e instanceof RegExpLiteral
or
s = e.(EncodingLiteralImpl).getValue()
or
@@ -319,6 +328,31 @@ private module Propagation {
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isSymbol(n, s))
}
predicate isRegExp(ExprCfgNode e, string s, string flags) {
isRegExpExprNoCfg(e.getExpr(), s, flags)
or
isRegExpExpr(e.getExpr().(ConstantReadAccess).getValue(), s, flags)
or
isRegExp(getSource(e), s, flags)
or
s = e.(StringlikeLiteralWithInterpolationCfgNode).getRegExpValue(flags)
}
private predicate isRegExpExprNoCfg(Expr e, string s, string flags) {
s = e.(StringlikeLiteralImpl).getStringValue() and
e.(RegExpLiteral).getFlagString() = flags
or
isRegExpExprNoCfg(e.(ConstantReadAccess).getValue(), s, flags)
}
predicate isRegExpExpr(Expr e, string s, string flags) {
isRegExpExprNoCfg(e, s, flags)
or
isRegExpExpr(e.(ConstantReadAccess).getValue(), s, flags)
or
forex(ExprCfgNode n | n = e.getAControlFlowNode() | isRegExp(n, s, flags))
}
predicate isBoolean(ExprCfgNode e, boolean b) {
isBooleanExprNoCfg(e.getExpr(), b)
or
@@ -388,9 +422,18 @@ private module Cached {
s = any(StringComponentImpl c).getValue()
} or
TSymbol(string s) { isString(_, s) or isSymbolExpr(_, s) } or
TRegExp(string s, string flags) {
isRegExp(_, s, flags)
or
isRegExpExpr(_, s, flags)
or
s = any(StringComponentImpl c).getValue() and flags = ""
} or
TBoolean(boolean b) { b in [false, true] } or
TNil()
class TStringlike = TString or TSymbol or TRegExp;
cached
ConstantValue getConstantValue(ExprCfgNode n) {
result.isInt(any(int i | isInt(n, i)))
@@ -411,6 +454,8 @@ private module Cached {
or
result.isSymbol(any(string s | isSymbol(n, s)))
or
exists(string s, string flags | isRegExp(n, s, flags) and result = TRegExp(s, flags))
or
result.isBoolean(any(boolean b | isBoolean(n, b)))
or
result.isNil() and
@@ -437,6 +482,8 @@ private module Cached {
or
result.isSymbol(any(string s | isSymbolExpr(e, s)))
or
exists(string s, string flags | isRegExpExpr(e, s, flags) and result = TRegExp(s, flags))
or
result.isBoolean(any(boolean b | isBooleanExpr(e, b)))
or
result.isNil() and

View File

@@ -189,7 +189,7 @@ class RedirectToCall extends ActionControllerContextCall {
/** Gets the `ActionControllerActionMethod` to redirect to, if any */
ActionControllerActionMethod getRedirectActionMethod() {
exists(string methodName |
this.getKeywordArgument("action").getConstantValue().isStringOrSymbol(methodName) and
this.getKeywordArgument("action").getConstantValue().isStringlikeValue(methodName) and
methodName = result.getName() and
result.getEnclosingModule() = this.getControllerClass()
)
@@ -225,7 +225,7 @@ pragma[nomagic]
private predicate actionControllerHasHelperMethodCall(ActionControllerControllerClass c, string name) {
exists(MethodCall mc |
mc.getMethodName() = "helper_method" and
mc.getAnArgument().getConstantValue().isStringOrSymbol(name) and
mc.getAnArgument().getConstantValue().isStringlikeValue(name) and
mc.getEnclosingModule() = c
)
}
@@ -317,7 +317,7 @@ class ActionControllerSkipForgeryProtectionCall extends CSRFProtectionSetting::R
call.getMethodName() = "skip_forgery_protection"
or
call.getMethodName() = "skip_before_action" and
call.getAnArgument().getConstantValue().isStringOrSymbol("verify_authenticity_token")
call.getAnArgument().getConstantValue().isStringlikeValue("verify_authenticity_token")
)
}

View File

@@ -165,14 +165,14 @@ module ActionDispatch {
override Location getLocation() { result = call.getLocation() }
override string getPathComponent() {
call.getKeywordArgument("path").getConstantValue().isStringOrSymbol(result)
call.getKeywordArgument("path").getConstantValue().isStringlikeValue(result)
or
not exists(call.getKeywordArgument("path")) and
call.getArgument(0).getConstantValue().isStringOrSymbol(result)
call.getArgument(0).getConstantValue().isStringlikeValue(result)
}
override string getControllerComponent() {
call.getKeywordArgument(["controller", "module"]).getConstantValue().isStringOrSymbol(result)
call.getKeywordArgument(["controller", "module"]).getConstantValue().isStringlikeValue(result)
}
}
@@ -201,7 +201,7 @@ module ActionDispatch {
MethodCall getDefiningMethodCall() { result = call }
override string getPathComponent() {
exists(string resource | call.getArgument(0).getConstantValue().isStringOrSymbol(resource) |
exists(string resource | call.getArgument(0).getConstantValue().isStringlikeValue(resource) |
result = resource + "/:" + singularize(resource) + "_id"
)
}
@@ -264,7 +264,7 @@ module ActionDispatch {
override string getControllerComponent() { result = this.getNamespace() }
private string getNamespace() {
call.getArgument(0).getConstantValue().isStringOrSymbol(result)
call.getArgument(0).getConstantValue().isStringlikeValue(result)
}
override string toString() { result = call.toString() }
@@ -482,11 +482,11 @@ module ActionDispatch {
override RouteBlock getParentBlock() { result = parentBlock }
override string getLastPathComponent() {
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
method.getArgument(0).getConstantValue().isStringlikeValue(result)
}
override string getLastControllerComponent() {
method.getKeywordArgument("controller").getConstantValue().isStringOrSymbol(result)
method.getKeywordArgument("controller").getConstantValue().isStringlikeValue(result)
or
not exists(method.getKeywordArgument("controller")) and
(
@@ -510,7 +510,7 @@ module ActionDispatch {
}
private string getActionString() {
method.getKeywordArgument("to").getConstantValue().isStringOrSymbol(result)
method.getKeywordArgument("to").getConstantValue().isStringlikeValue(result)
or
method.getKeywordArgument("to").(MethodCall).getMethodName() = "redirect" and
result = "<redirect>#<redirect>"
@@ -518,7 +518,7 @@ module ActionDispatch {
override string getAction() {
// get "/photos", action: "index"
method.getKeywordArgument("action").getConstantValue().isStringOrSymbol(result)
method.getKeywordArgument("action").getConstantValue().isStringlikeValue(result)
or
not exists(method.getKeywordArgument("action")) and
(
@@ -533,7 +533,7 @@ module ActionDispatch {
or
// get :some_action
not exists(this.getActionString()) and
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
method.getArgument(0).getConstantValue().isStringlikeValue(result)
)
}
@@ -580,7 +580,7 @@ module ActionDispatch {
ResourcesRoute() {
exists(string resource |
this = TResourcesRoute(parent, method, action) and
method.getArgument(0).getConstantValue().isStringOrSymbol(resource) and
method.getArgument(0).getConstantValue().isStringlikeValue(resource) and
isDefaultResourceRoute(resource, httpMethod, pathComponent, action)
)
}
@@ -592,7 +592,7 @@ module ActionDispatch {
override string getLastPathComponent() { result = pathComponent }
override string getLastControllerComponent() {
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
method.getArgument(0).getConstantValue().isStringlikeValue(result)
}
override string getAction() { result = action }
@@ -618,7 +618,7 @@ module ActionDispatch {
SingularResourceRoute() {
exists(string resource |
this = TResourceRoute(parent, method, action) and
method.getArgument(0).getConstantValue().isStringOrSymbol(resource) and
method.getArgument(0).getConstantValue().isStringlikeValue(resource) and
isDefaultSingularResourceRoute(resource, httpMethod, pathComponent, action)
)
}
@@ -630,7 +630,7 @@ module ActionDispatch {
override string getLastPathComponent() { result = pathComponent }
override string getLastControllerComponent() {
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
method.getArgument(0).getConstantValue().isStringlikeValue(result)
}
override string getAction() { result = action }
@@ -663,25 +663,25 @@ module ActionDispatch {
override string getLastPathComponent() {
[method.getArgument(0), method.getArgument(0).(Pair).getKey()]
.getConstantValue()
.isStringOrSymbol(result)
.isStringlikeValue(result)
}
override string getLastControllerComponent() {
result =
extractController(method.getKeywordArgument("to").getConstantValue().getStringOrSymbol()) or
method.getKeywordArgument("controller").getConstantValue().isStringOrSymbol(result) or
extractController(method.getKeywordArgument("to").getConstantValue().getStringlikeValue()) or
method.getKeywordArgument("controller").getConstantValue().isStringlikeValue(result) or
result =
extractController(method
.getArgument(0)
.(Pair)
.getValue()
.getConstantValue()
.getStringOrSymbol())
.getStringlikeValue())
}
override string getHttpMethod() {
exists(string via |
method.getKeywordArgument("via").getConstantValue().isStringOrSymbol(via)
method.getKeywordArgument("via").getConstantValue().isStringlikeValue(via)
|
via = "all" and result = anyHttpMethod()
or
@@ -694,14 +694,20 @@ module ActionDispatch {
.(ArrayLiteral)
.getElement(_)
.getConstantValue()
.getStringOrSymbol()
.getStringlikeValue()
}
override string getAction() {
result = extractAction(method.getKeywordArgument("to").getConstantValue().getStringOrSymbol()) or
method.getKeywordArgument("action").getConstantValue().isStringOrSymbol(result) or
result =
extractAction(method.getArgument(0).(Pair).getValue().getConstantValue().getStringOrSymbol())
extractAction(method.getKeywordArgument("to").getConstantValue().getStringlikeValue()) or
method.getKeywordArgument("action").getConstantValue().isStringlikeValue(result) or
result =
extractAction(method
.getArgument(0)
.(Pair)
.getValue()
.getConstantValue()
.getStringlikeValue())
}
}
@@ -802,7 +808,7 @@ module ActionDispatch {
not exists(m.getKeywordArgument("only"))
or
exists(Expr only | only = m.getKeywordArgument("only") |
[only.(ArrayLiteral).getElement(_), only].getConstantValue().isStringOrSymbol(action)
[only.(ArrayLiteral).getElement(_), only].getConstantValue().isStringlikeValue(action)
)
) and
// Respect the `except` keyword argument, which removes actions from the default set.
@@ -810,7 +816,7 @@ module ActionDispatch {
not exists(m.getKeywordArgument("except"))
or
exists(Expr except | except = m.getKeywordArgument("except") |
[except.(ArrayLiteral).getElement(_), except].getConstantValue().getStringOrSymbol() !=
[except.(ArrayLiteral).getElement(_), except].getConstantValue().getStringlikeValue() !=
action
)
)

View File

@@ -95,7 +95,7 @@ abstract class RenderCall extends MethodCall {
}
private string getTemplatePathValue() {
result = this.getTemplatePathArgument().getConstantValue().getStringOrSymbol()
result = this.getTemplatePathArgument().getConstantValue().getStringlikeValue()
}
// everything up to and including the final slash, but ignoring any leading slash

View File

@@ -28,7 +28,7 @@ private DataFlow::Node ioInstance() {
// will execute a shell command and read its output rather than reading from the
// filesystem.
private predicate pathArgSpawnsSubprocess(Expr arg) {
arg.getConstantValue().getStringOrSymbol().charAt(0) = "|"
arg.getConstantValue().getStringlikeValue().charAt(0) = "|"
}
private DataFlow::Node fileInstanceInstantiation() {

View File

@@ -248,7 +248,7 @@ class GraphqlFieldDefinitionMethodCall extends GraphqlSchemaObjectClassMethodCal
GraphqlFieldDefinitionMethodCall() { this.getMethodName() = "field" }
/** Gets the name of this GraphQL field. */
string getFieldName() { result = this.getArgument(0).getConstantValue().getStringOrSymbol() }
string getFieldName() { result = this.getArgument(0).getConstantValue().getStringlikeValue() }
}
/**
@@ -284,7 +284,7 @@ private class GraphqlFieldArgumentDefinitionMethodCall extends GraphqlSchemaObje
string getFieldName() { result = this.getFieldDefinition().getFieldName() }
/** Gets the name of the argument (i.e. the first argument to this `argument` method call) */
string getArgumentName() { result = this.getArgument(0).getConstantValue().getStringOrSymbol() }
string getArgumentName() { result = this.getArgument(0).getConstantValue().getStringlikeValue() }
}
/**
@@ -333,7 +333,9 @@ class GraphqlFieldResolutionMethod extends Method, HTTP::Server::RequestHandler:
exists(GraphqlFieldDefinitionMethodCall defn |
// field :foo, resolver_method: :custom_method
// def custom_method(...)
defn.getKeywordArgument("resolver_method").getConstantValue().isStringOrSymbol(this.getName())
defn.getKeywordArgument("resolver_method")
.getConstantValue()
.isStringlikeValue(this.getName())
or
// field :foo
// def foo(...)
@@ -344,7 +346,10 @@ class GraphqlFieldResolutionMethod extends Method, HTTP::Server::RequestHandler:
/** Gets the method call which is the definition of the field corresponding to this resolver method. */
GraphqlFieldDefinitionMethodCall getDefinition() {
result.getKeywordArgument("resolver_method").getConstantValue().isStringOrSymbol(this.getName())
result
.getKeywordArgument("resolver_method")
.getConstantValue()
.isStringlikeValue(this.getName())
or
not exists(result.getKeywordArgument("resolver_method").(SymbolLiteral)) and
result.getFieldName() = this.getName()

View File

@@ -164,7 +164,7 @@ private module Settings {
* A node that sets a Stringlike value.
*/
class StringlikeSetting extends LiteralSetting {
override ConstantValue::ConstantStringValue value;
override ConstantValue::ConstantStringlikeValue value;
}
/**
@@ -172,12 +172,11 @@ private module Settings {
*/
class NillableStringlikeSetting extends LiteralSetting {
NillableStringlikeSetting() {
value instanceof ConstantValue::ConstantStringValue or
value instanceof ConstantValue::ConstantSymbolValue or
value instanceof ConstantValue::ConstantStringlikeValue or
value instanceof ConstantValue::ConstantNilValue
}
string getStringValue() { result = value.getStringOrSymbol() }
string getStringValue() { result = value.getStringlikeValue() }
predicate isNilValue() { value.isNil() }
}

View File

@@ -59,7 +59,7 @@ private class LibXmlRubyXmlParserCall extends XmlParserCall::Range, DataFlow::Ca
exists(CfgNodes::ExprNodes::PairCfgNode pair |
pair =
this.getArgument(1).asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
pair.getKey().getConstantValue().isStringOrSymbol("options") and
pair.getKey().getConstantValue().isStringlikeValue("options") and
pair.getValue() =
[
trackEnableFeature(TNOENT()), trackEnableFeature(TDTDLOAD()),

View File

@@ -125,7 +125,7 @@ private predicate setsDefaultVerification(DataFlow::CallNode callNode, boolean v
private predicate isSslVerifyPeerLiteral(DataFlow::Node node) {
exists(DataFlow::LocalSourceNode literal |
literal.asExpr().getExpr().getConstantValue().isStringOrSymbol("ssl_verify_peer") and
literal.asExpr().getExpr().getConstantValue().isStringlikeValue("ssl_verify_peer") and
literal.flowsTo(node)
)
}

View File

@@ -96,7 +96,7 @@ private predicate isSslOptionsPairDisablingValidation(CfgNodes::ExprNodes::PairC
/** Holds if `node` represents the symbol literal with the given `valueText`. */
private predicate isSymbolLiteral(DataFlow::Node node, string valueText) {
exists(DataFlow::LocalSourceNode literal |
literal.asExpr().getExpr().getConstantValue().isStringOrSymbol(valueText) and
literal.asExpr().getExpr().getConstantValue().isStringlikeValue(valueText) and
literal.flowsTo(node)
)
}

View File

@@ -79,7 +79,7 @@ class HttpartyRequest extends HTTP::Client::Request::Range {
/** Holds if `node` represents the symbol literal `verify` or `verify_peer`. */
private predicate isVerifyLiteral(DataFlow::Node node) {
exists(DataFlow::LocalSourceNode literal |
literal.asExpr().getExpr().getConstantValue().isStringOrSymbol(["verify", "verify_peer"]) and
literal.asExpr().getExpr().getConstantValue().isStringlikeValue(["verify", "verify_peer"]) and
literal.flowsTo(node)
)
}

View File

@@ -117,7 +117,7 @@ private predicate isSslVerifyModeNonePair(CfgNodes::ExprNodes::PairCfgNode p) {
/** Holds if `node` can represent the symbol literal `:ssl_verify_mode`. */
private predicate isSslVerifyModeLiteral(DataFlow::Node node) {
exists(DataFlow::LocalSourceNode literal |
literal.asExpr().getExpr().getConstantValue().isStringOrSymbol("ssl_verify_mode") and
literal.asExpr().getExpr().getConstantValue().isStringlikeValue("ssl_verify_mode") and
literal.flowsTo(node)
)
}

View File

@@ -86,7 +86,7 @@ private predicate isVerifySslNonePair(CfgNodes::ExprNodes::PairCfgNode p) {
/** Holds if `node` can represent the symbol literal `:verify_ssl`. */
private predicate isSslVerifyModeLiteral(DataFlow::Node node) {
exists(DataFlow::LocalSourceNode literal |
literal.asExpr().getExpr().getConstantValue().isStringOrSymbol("verify_ssl") and
literal.asExpr().getExpr().getConstantValue().isStringlikeValue("verify_ssl") and
literal.flowsTo(node)
)
}

View File

@@ -67,7 +67,7 @@ private predicate isSslVerifyPeerFalsePair(CfgNodes::ExprNodes::PairCfgNode p) {
/** Holds if `node` represents the symbol literal `verify` or `verify_peer`. */
private predicate isSslVerifyPeerLiteral(DataFlow::Node node) {
exists(DataFlow::LocalSourceNode literal |
literal.asExpr().getExpr().getConstantValue().isStringOrSymbol("ssl_verifypeer") and
literal.asExpr().getExpr().getConstantValue().isStringlikeValue("ssl_verifypeer") and
literal.flowsTo(node)
)
}

View File

@@ -5,7 +5,7 @@
* the `x` (free-spacing) flag.
*/
private import codeql.ruby.ast.Literal as AST
private import codeql.ruby.AST as AST
private import codeql.Locations
/**
@@ -254,7 +254,11 @@ abstract class RegExp extends AST::StringlikeLiteral {
}
/** Gets the text of this regex */
string getText() { result = this.getConstantValue().getString() }
string getText() {
exists(AST::ConstantValue c | c = this.getConstantValue() |
result = [this.getConstantValue().getString(), this.getConstantValue().getRegExp()]
)
}
/** Gets the `i`th character of this regex */
string getChar(int i) { result = this.getText().charAt(i) }

View File

@@ -74,7 +74,7 @@ module UnsafeDeserialization {
}
private predicate isOjModePair(CfgNodes::ExprNodes::PairCfgNode p, string modeValue) {
p.getKey().getConstantValue().isStringOrSymbol("mode") and
p.getKey().getConstantValue().isStringlikeValue("mode") and
exists(DataFlow::LocalSourceNode symbolLiteral, DataFlow::Node value |
symbolLiteral.asExpr().getExpr().getConstantValue().isSymbol(modeValue) and
symbolLiteral.flowsTo(value) and

View File

@@ -141,7 +141,7 @@ private module Shared {
exists(RenderCall call, Pair kvPair |
call.getLocals().getAKeyValuePair() = kvPair and
kvPair.getValue() = value and
kvPair.getKey().getConstantValue().isStringOrSymbol(hashKey) and
kvPair.getKey().getConstantValue().isStringlikeValue(hashKey) and
call.getTemplateFile() = erb
)
}
@@ -154,7 +154,7 @@ private module Shared {
argNode.asExpr() = refNode.getArgument(0) and
refNode.getReceiver().getExpr().(MethodCall).getMethodName() = "local_assigns" and
argNode.getALocalSource() = DataFlow::exprNode(strNode) and
strNode.getExpr().getConstantValue().isStringOrSymbol(hashKey) and
strNode.getExpr().getConstantValue().isStringlikeValue(hashKey) and
erb = refNode.getFile()
)
}

View File

@@ -36,7 +36,7 @@ module CleartextSources {
* sensitive data with a call to `sub`.
*/
private predicate effectiveSubRegExp(CfgNodes::ExprNodes::RegExpLiteralCfgNode re) {
re.getConstantValue().getStringOrSymbol().matches([".*", ".+"])
re.getConstantValue().getStringlikeValue().matches([".*", ".+"])
}
/**
@@ -44,7 +44,7 @@ module CleartextSources {
* sensitive data with a call to `gsub`.
*/
private predicate effectiveGsubRegExp(CfgNodes::ExprNodes::RegExpLiteralCfgNode re) {
re.getConstantValue().getStringOrSymbol().matches(".")
re.getConstantValue().getStringlikeValue().matches(".")
}
/**
@@ -112,7 +112,7 @@ module CleartextSources {
.(CfgNodes::ExprNodes::ElementReferenceCfgNode)
.getArgument(0)
.getConstantValue()
.getStringOrSymbol() = name
.getStringlikeValue() = name
or
// calling a non-sensitive method
this.(DataFlow::CallNode).getMethodName() = name
@@ -123,7 +123,7 @@ module CleartextSources {
.(CfgNodes::ExprNodes::ElementReferenceCfgNode)
.getReceiver()
.getConstantValue()
.getStringOrSymbol()
.getStringlikeValue()
.regexpMatch("(?is).*(messages|strings).*")
}
}
@@ -146,7 +146,7 @@ module CleartextSources {
private predicate hashKeyWrite(DataFlow::CallNode writeNode, string name, DataFlow::Node val) {
writeNode.asExpr().getExpr() instanceof SetterMethodCall and
// hash[name]
writeNode.getArgument(0).asExpr().getConstantValue().getStringOrSymbol() = name and
writeNode.getArgument(0).asExpr().getConstantValue().getStringlikeValue() = name and
// val
writeNode.getArgument(1).asExpr().(CfgNodes::ExprNodes::AssignExprCfgNode).getRhs() =
val.asExpr()
@@ -203,7 +203,7 @@ module CleartextSources {
exists(CfgNodes::ExprNodes::PairCfgNode p |
this.asExpr() = lit and p = lit.getAKeyValuePair()
|
p.getKey().getConstantValue().getStringOrSymbol() = name and
p.getKey().getConstantValue().getStringlikeValue() = name and
p.getValue() = val.asExpr()
)
)
@@ -266,7 +266,7 @@ module CleartextSources {
// from `hsh[password] = "changeme"` to a `hsh[password]` read
nodeFrom.(HashKeyWritePasswordSource).getName() = name and
nodeTo.asExpr().getExpr() = ref and
ref.getArgument(0).getConstantValue().getStringOrSymbol() = name and
ref.getArgument(0).getConstantValue().getStringlikeValue() = name and
nodeFrom.(HashKeyWritePasswordSource).getVariable() = hashVar and
ref.getReceiver().(VariableReadAccess).getVariable() = hashVar and
nodeFrom.asExpr().getASuccessor*() = nodeTo.asExpr()

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,10 @@
import ruby
import codeql.ruby.controlflow.CfgNodes
query predicate exprValue(Expr e, ConstantValue v) { v = e.getConstantValue() }
query predicate exprValue(Expr e, ConstantValue v, string t) {
v = e.getConstantValue() and t = v.getValueType()
}
query predicate exprCfgNodeValue(ExprCfgNode n, ConstantValue v) { v = n.getConstantValue() }
query predicate exprCfgNodeValue(ExprCfgNode n, ConstantValue v, string t) {
v = n.getConstantValue() and t = v.getValueType()
}

View File

@@ -89,12 +89,12 @@ stringlikeLiterals
| escapes.rb:38:1:38:6 | "\\C-?" | C-? | string |
| escapes.rb:43:5:43:9 | "\\\\." | \\. | string |
| escapes.rb:44:1:44:6 | "#{...}" | \\. | string |
| escapes.rb:48:1:48:4 | /\\n/ | \\n | string |
| escapes.rb:49:1:49:4 | /\\p/ | \\p | string |
| escapes.rb:50:1:50:8 | /\\u0061/ | \\u0061 | string |
| escapes.rb:48:1:48:4 | /\\n/ | \\n | regexp |
| escapes.rb:49:1:49:4 | /\\p/ | \\p | regexp |
| escapes.rb:50:1:50:8 | /\\u0061/ | \\u0061 | regexp |
| escapes.rb:53:5:53:9 | "\\\\." | \\. | string |
| escapes.rb:54:5:54:8 | /\\./ | \\. | string |
| escapes.rb:55:1:55:10 | /#{...}#{...}/ | \\.\\. | string |
| escapes.rb:54:5:54:8 | /\\./ | \\. | regexp |
| escapes.rb:55:1:55:10 | /#{...}#{...}/ | \\.\\. | regexp |
| escapes.rb:58:4:58:9 | "foo \\n" | foo\\n | string |
| escapes.rb:58:11:58:13 | "bar" | bar | string |
| escapes.rb:61:1:61:5 | :"\\'" | ' | symbol |

View File

@@ -18,7 +18,7 @@ query predicate regexpEscapeSequenceComponents(RegExpEscapeSequenceComponent c,
}
query predicate stringlikeLiterals(StringlikeLiteral l, string value, string kind) {
value = l.getConstantValue().getString() and kind = "string"
or
value = l.getConstantValue().getSymbol() and kind = "symbol"
exists(ConstantValue v |
v = l.getConstantValue() and value = v.getStringlikeValue() and kind = v.getValueType()
)
}

View File

@@ -1,7 +1,7 @@
import ruby
private string getValueText(MethodName m) {
result = m.getConstantValue().getStringOrSymbol()
result = m.getConstantValue().getStringlikeValue()
or
not exists(m.getConstantValue()) and result = "(none)"
}