mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge pull request #7750 from hvitved/ruby/desugar-hash-literals
Ruby: Desugar hash literals
This commit is contained in:
@@ -2,6 +2,7 @@ private import codeql.ruby.AST
|
||||
private import codeql.ruby.security.performance.RegExpTreeView as RETV
|
||||
private import internal.AST
|
||||
private import internal.Constant
|
||||
private import internal.Literal
|
||||
private import internal.Scope
|
||||
private import internal.TreeSitter
|
||||
private import codeql.ruby.controlflow.CfgNodes
|
||||
@@ -36,9 +37,9 @@ class NumericLiteral extends Literal, TNumericLiteral { }
|
||||
* 0xff
|
||||
* ```
|
||||
*/
|
||||
class IntegerLiteral extends NumericLiteral, TIntegerLiteral {
|
||||
class IntegerLiteral extends NumericLiteral instanceof IntegerLiteralImpl {
|
||||
/** Gets the numerical value of this integer literal. */
|
||||
int getValue() { none() }
|
||||
final int getValue() { result = super.getValueImpl() }
|
||||
|
||||
final override ConstantValue::ConstantIntegerValue getConstantValue() {
|
||||
result.isInt(this.getValue())
|
||||
@@ -47,76 +48,6 @@ class IntegerLiteral extends NumericLiteral, TIntegerLiteral {
|
||||
final override string getAPrimaryQlClass() { result = "IntegerLiteral" }
|
||||
}
|
||||
|
||||
private int parseInteger(Ruby::Integer i) {
|
||||
exists(string s | s = i.getValue().toLowerCase().replaceAll("_", "") |
|
||||
s.charAt(0) != "0" and
|
||||
result = s.toInt()
|
||||
or
|
||||
exists(string str, string values, int shift |
|
||||
s.matches("0b%") and
|
||||
values = "01" and
|
||||
str = s.suffix(2) and
|
||||
shift = 1
|
||||
or
|
||||
s.matches("0x%") and
|
||||
values = "0123456789abcdef" and
|
||||
str = s.suffix(2) and
|
||||
shift = 4
|
||||
or
|
||||
s.charAt(0) = "0" and
|
||||
not s.charAt(1) = ["b", "x", "o"] and
|
||||
values = "01234567" and
|
||||
str = s.suffix(1) and
|
||||
shift = 3
|
||||
or
|
||||
s.matches("0o%") and
|
||||
values = "01234567" and
|
||||
str = s.suffix(2) and
|
||||
shift = 3
|
||||
|
|
||||
result =
|
||||
sum(int index, string c, int v, int exp |
|
||||
c = str.charAt(index) and
|
||||
v = values.indexOf(c.toLowerCase()) and
|
||||
exp = str.length() - index - 1
|
||||
|
|
||||
v.bitShiftLeft((str.length() - index - 1) * shift)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredIntegerLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredInt(int i) { i = any(IntegerLiteral il).getValue() }
|
||||
}
|
||||
|
||||
private class IntegerLiteralReal extends IntegerLiteral, TIntegerLiteralReal {
|
||||
private Ruby::Integer g;
|
||||
|
||||
IntegerLiteralReal() { this = TIntegerLiteralReal(g) }
|
||||
|
||||
final override int getValue() { result = parseInteger(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
private class IntegerLiteralSynth extends IntegerLiteral, TIntegerLiteralSynth {
|
||||
private int value;
|
||||
|
||||
IntegerLiteralSynth() { this = TIntegerLiteralSynth(_, _, value) }
|
||||
|
||||
final override int getValue() { result = value }
|
||||
|
||||
final override string toString() { result = value.toString() }
|
||||
}
|
||||
|
||||
// TODO: implement properly
|
||||
private float parseFloat(Ruby::Float f) { result = f.getValue().toFloat() }
|
||||
|
||||
private class RequiredFloatConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredFloat(float f) { f = parseFloat(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A floating-point literal.
|
||||
*
|
||||
@@ -139,26 +70,6 @@ class FloatLiteral extends NumericLiteral, TFloatLiteral {
|
||||
final override string getAPrimaryQlClass() { result = "FloatLiteral" }
|
||||
}
|
||||
|
||||
private predicate isRationalValue(Ruby::Rational r, int numerator, int denominator) {
|
||||
numerator = parseInteger(r.getChild()) and
|
||||
denominator = 1
|
||||
or
|
||||
exists(Ruby::Float f, string regex, string before, string after |
|
||||
f = r.getChild() and
|
||||
regex = "([^.]*)\\.(.*)" and
|
||||
before = f.getValue().regexpCapture(regex, 1) and
|
||||
after = f.getValue().regexpCapture(regex, 2) and
|
||||
numerator = before.toInt() * denominator + after.toInt() and
|
||||
denominator = 10.pow(after.length())
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredRationalConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredRational(int numerator, int denominator) {
|
||||
isRationalValue(_, numerator, denominator)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A rational literal.
|
||||
*
|
||||
@@ -183,20 +94,6 @@ class RationalLiteral extends NumericLiteral, TRationalLiteral {
|
||||
final override string getAPrimaryQlClass() { result = "RationalLiteral" }
|
||||
}
|
||||
|
||||
private float getComplexValue(Ruby::Complex c) {
|
||||
exists(string s |
|
||||
s = c.getValue() and
|
||||
result = s.prefix(s.length() - 1).toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredComplexConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredComplex(float real, float imaginary) {
|
||||
real = 0 and
|
||||
imaginary = getComplexValue(_)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A complex literal.
|
||||
*
|
||||
@@ -261,30 +158,6 @@ class BooleanLiteral extends Literal, TBooleanLiteral {
|
||||
}
|
||||
}
|
||||
|
||||
private class TrueLiteral extends BooleanLiteral, TTrueLiteral {
|
||||
private Ruby::True g;
|
||||
|
||||
TrueLiteral() { this = TTrueLiteral(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override predicate isTrue() { any() }
|
||||
}
|
||||
|
||||
private class FalseLiteral extends BooleanLiteral, TFalseLiteral {
|
||||
private Ruby::False g;
|
||||
|
||||
FalseLiteral() { this = TFalseLiteral(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override predicate isFalse() { any() }
|
||||
}
|
||||
|
||||
private class RequiredEncodingLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = "UTF-8" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `__ENCODING__` literal.
|
||||
*/
|
||||
@@ -297,10 +170,6 @@ class EncodingLiteral extends Literal, TEncoding {
|
||||
override ConstantValue::ConstantStringValue getConstantValue() { result.isString("UTF-8") }
|
||||
}
|
||||
|
||||
private class RequiredLineLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredInt(int i) { i = any(LineLiteral ll).getLocation().getStartLine() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `__LINE__` literal.
|
||||
*/
|
||||
@@ -314,12 +183,6 @@ class LineLiteral extends Literal, TLine {
|
||||
}
|
||||
}
|
||||
|
||||
private class RequiredFileLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) {
|
||||
s = any(FileLiteral fl).getLocation().getFile().getAbsolutePath()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `__FILE__` literal.
|
||||
*/
|
||||
@@ -350,12 +213,6 @@ class StringComponent extends AstNode, TStringComponent {
|
||||
ConstantValue::ConstantStringValue getConstantValue() { none() }
|
||||
}
|
||||
|
||||
private class RequiredStringTextComponentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) {
|
||||
s = any(Ruby::Token t | exists(TStringTextComponentNonRegexp(t))).getValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A component of a string (or string-like) literal that is simply text.
|
||||
*
|
||||
@@ -382,12 +239,6 @@ class StringTextComponent extends StringComponent, TStringTextComponentNonRegexp
|
||||
final override string getAPrimaryQlClass() { result = "StringTextComponent" }
|
||||
}
|
||||
|
||||
private class RequiredStringEscapeSequenceComponentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) {
|
||||
s = any(Ruby::Token t | exists(TStringEscapeSequenceComponentNonRegexp(t))).getValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An escape sequence component of a string or string-like literal.
|
||||
*/
|
||||
@@ -425,10 +276,6 @@ class StringInterpolationComponent extends StringComponent, StmtSequence,
|
||||
final override string getAPrimaryQlClass() { result = "StringInterpolationComponent" }
|
||||
}
|
||||
|
||||
private class TRegExpComponent =
|
||||
TStringTextComponentRegexp or TStringEscapeSequenceComponentRegexp or
|
||||
TStringInterpolationComponentRegexp;
|
||||
|
||||
/**
|
||||
* The base class for a component of a regular expression literal.
|
||||
*/
|
||||
@@ -444,20 +291,6 @@ class RegExpComponent extends AstNode, TRegExpComponent {
|
||||
ConstantValue::ConstantStringValue getConstantValue() { none() }
|
||||
}
|
||||
|
||||
// Exclude components that are children of a free-spacing regex.
|
||||
// We do this because `ParseRegExp.qll` cannot handle free-spacing regexes.
|
||||
private string getRegExpTextComponentValue(RegExpTextComponent c) {
|
||||
exists(Ruby::Token t |
|
||||
c = TStringTextComponentRegexp(t) and
|
||||
not c.getParent().(RegExpLiteral).hasFreeSpacingFlag() and
|
||||
result = t.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredRegExpTextComponentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = getRegExpTextComponentValue(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A component of a regex literal that is simply text.
|
||||
*
|
||||
@@ -484,20 +317,6 @@ class RegExpTextComponent extends RegExpComponent, TStringTextComponentRegexp {
|
||||
final override string getAPrimaryQlClass() { result = "RegExpTextComponent" }
|
||||
}
|
||||
|
||||
// Exclude components that are children of a free-spacing regex.
|
||||
// We do this because `ParseRegExp.qll` cannot handle free-spacing regexes.
|
||||
private string getRegExpEscapeSequenceComponentValue(RegExpEscapeSequenceComponent c) {
|
||||
exists(Ruby::EscapeSequence e |
|
||||
c = TStringEscapeSequenceComponentRegexp(e) and
|
||||
not c.getParent().(RegExpLiteral).hasFreeSpacingFlag() and
|
||||
result = e.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredRegExpEscapeSequenceComponentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = getRegExpEscapeSequenceComponentValue(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An escape sequence component of a regex literal.
|
||||
*/
|
||||
@@ -662,22 +481,6 @@ class StringLiteral extends StringlikeLiteral, TStringLiteral {
|
||||
final override string getAPrimaryQlClass() { result = "StringLiteral" }
|
||||
}
|
||||
|
||||
private class RegularStringLiteral extends StringLiteral, TRegularStringLiteral {
|
||||
private Ruby::String g;
|
||||
|
||||
RegularStringLiteral() { this = TRegularStringLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) }
|
||||
}
|
||||
|
||||
private class BareStringLiteral extends StringLiteral, TBareStringLiteral {
|
||||
private Ruby::BareString g;
|
||||
|
||||
BareStringLiteral() { this = TBareStringLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A regular expression literal.
|
||||
*
|
||||
@@ -762,13 +565,6 @@ class SymbolLiteral extends StringlikeLiteral, TSymbolLiteral {
|
||||
}
|
||||
}
|
||||
|
||||
// Tree-sitter gives us value text including the colon, which we skip.
|
||||
private string getSimpleSymbolValue(Ruby::SimpleSymbol ss) { result = ss.getValue().suffix(1) }
|
||||
|
||||
private class RequiredSimpleSymbolConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredSymbol(string s) { s = getSimpleSymbolValue(_) }
|
||||
}
|
||||
|
||||
private class SimpleSymbolLiteral extends SymbolLiteral, TSimpleSymbolLiteral {
|
||||
private Ruby::SimpleSymbol g;
|
||||
|
||||
@@ -781,40 +577,6 @@ private class SimpleSymbolLiteral extends SymbolLiteral, TSimpleSymbolLiteral {
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
private class ComplexSymbolLiteral extends SymbolLiteral, TComplexSymbolLiteral { }
|
||||
|
||||
private class DelimitedSymbolLiteral extends ComplexSymbolLiteral, TDelimitedSymbolLiteral {
|
||||
private Ruby::DelimitedSymbol g;
|
||||
|
||||
DelimitedSymbolLiteral() { this = TDelimitedSymbolLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) }
|
||||
}
|
||||
|
||||
private class BareSymbolLiteral extends ComplexSymbolLiteral, TBareSymbolLiteral {
|
||||
private Ruby::BareSymbol g;
|
||||
|
||||
BareSymbolLiteral() { this = TBareSymbolLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) }
|
||||
}
|
||||
|
||||
private class RequiredHashKeySymbolConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredSymbol(string s) { s = any(Ruby::HashKeySymbol h).getValue() }
|
||||
}
|
||||
|
||||
private class HashKeySymbolLiteral extends SymbolLiteral, THashKeySymbolLiteral {
|
||||
private Ruby::HashKeySymbol g;
|
||||
|
||||
HashKeySymbolLiteral() { this = THashKeySymbolLiteral(g) }
|
||||
|
||||
final override ConstantValue::ConstantSymbolValue getConstantValue() {
|
||||
result.isSymbol(g.getValue())
|
||||
}
|
||||
|
||||
final override string toString() { result = ":" + g.getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A subshell literal.
|
||||
*
|
||||
@@ -933,67 +695,25 @@ class HereDoc extends StringlikeLiteral, THereDoc {
|
||||
* %i(foo bar)
|
||||
* ```
|
||||
*/
|
||||
class ArrayLiteral extends Literal, TArrayLiteral {
|
||||
class ArrayLiteral extends Literal instanceof ArrayLiteralImpl {
|
||||
final override string getAPrimaryQlClass() { result = "ArrayLiteral" }
|
||||
|
||||
/** Gets the `n`th element in this array literal. */
|
||||
final Expr getElement(int n) { result = this.(ArrayLiteralImpl).getElementImpl(n) }
|
||||
final Expr getElement(int n) { result = super.getElementImpl(n) }
|
||||
|
||||
/** Gets an element in this array literal. */
|
||||
final Expr getAnElement() { result = this.getElement(_) }
|
||||
|
||||
/** Gets the number of elements in this array literal. */
|
||||
final int getNumberOfElements() { result = this.(ArrayLiteralImpl).getNumberOfElementsImpl() }
|
||||
final int getNumberOfElements() { result = super.getNumberOfElementsImpl() }
|
||||
|
||||
final override AstNode getAChild(string pred) {
|
||||
result = super.getAChild(pred)
|
||||
result = Literal.super.getAChild(pred)
|
||||
or
|
||||
pred = "getElement" and result = this.getElement(_)
|
||||
}
|
||||
}
|
||||
|
||||
abstract private class ArrayLiteralImpl extends ArrayLiteral {
|
||||
abstract Expr getElementImpl(int n);
|
||||
|
||||
abstract int getNumberOfElementsImpl();
|
||||
}
|
||||
|
||||
private class RegularArrayLiteral extends ArrayLiteralImpl, TRegularArrayLiteral {
|
||||
private Ruby::Array g;
|
||||
|
||||
RegularArrayLiteral() { this = TRegularArrayLiteral(g) }
|
||||
|
||||
final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) }
|
||||
|
||||
final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) }
|
||||
|
||||
final override string toString() { result = "[...]" }
|
||||
}
|
||||
|
||||
private class StringArrayLiteral extends ArrayLiteralImpl, TStringArrayLiteral {
|
||||
private Ruby::StringArray g;
|
||||
|
||||
StringArrayLiteral() { this = TStringArrayLiteral(g) }
|
||||
|
||||
final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) }
|
||||
|
||||
final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) }
|
||||
|
||||
final override string toString() { result = "%w(...)" }
|
||||
}
|
||||
|
||||
private class SymbolArrayLiteral extends ArrayLiteralImpl, TSymbolArrayLiteral {
|
||||
private Ruby::SymbolArray g;
|
||||
|
||||
SymbolArrayLiteral() { this = TSymbolArrayLiteral(g) }
|
||||
|
||||
final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) }
|
||||
|
||||
final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) }
|
||||
|
||||
final override string toString() { result = "%i(...)" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A hash literal.
|
||||
*
|
||||
@@ -1001,7 +721,7 @@ private class SymbolArrayLiteral extends ArrayLiteralImpl, TSymbolArrayLiteral {
|
||||
* { foo: 123, bar: 456 }
|
||||
* ```
|
||||
*/
|
||||
class HashLiteral extends Literal, THashLiteral {
|
||||
class HashLiteral extends Literal instanceof HashLiteralImpl {
|
||||
private Ruby::Hash g;
|
||||
|
||||
HashLiteral() { this = THashLiteral(g) }
|
||||
@@ -1027,12 +747,12 @@ class HashLiteral extends Literal, THashLiteral {
|
||||
final Pair getAKeyValuePair() { result = this.getAnElement() }
|
||||
|
||||
/** Gets the number of elements in this hash literal. */
|
||||
final int getNumberOfElements() { result = count(this.getAnElement()) }
|
||||
final int getNumberOfElements() { result = super.getNumberOfElementsImpl() }
|
||||
|
||||
final override string toString() { result = "{...}" }
|
||||
|
||||
final override AstNode getAChild(string pred) {
|
||||
result = super.getAChild(pred)
|
||||
result = Literal.super.getAChild(pred)
|
||||
or
|
||||
pred = "getElement" and result = this.getElement(_)
|
||||
}
|
||||
@@ -1086,34 +806,6 @@ class RangeLiteral extends Literal, TRangeLiteral {
|
||||
}
|
||||
}
|
||||
|
||||
private class RangeLiteralReal extends RangeLiteral, TRangeLiteralReal {
|
||||
private Ruby::Range g;
|
||||
|
||||
RangeLiteralReal() { this = TRangeLiteralReal(g) }
|
||||
|
||||
final override Expr getBegin() { toGenerated(result) = g.getBegin() }
|
||||
|
||||
final override Expr getEnd() { toGenerated(result) = g.getEnd() }
|
||||
|
||||
final override predicate isInclusive() { g instanceof @ruby_range_dotdot }
|
||||
|
||||
final override predicate isExclusive() { g instanceof @ruby_range_dotdotdot }
|
||||
}
|
||||
|
||||
private class RangeLiteralSynth extends RangeLiteral, TRangeLiteralSynth {
|
||||
private boolean inclusive;
|
||||
|
||||
RangeLiteralSynth() { this = TRangeLiteralSynth(_, _, inclusive) }
|
||||
|
||||
final override Expr getBegin() { result = TIntegerLiteralSynth(this, 0, _) }
|
||||
|
||||
final override Expr getEnd() { result = TIntegerLiteralSynth(this, 1, _) }
|
||||
|
||||
final override predicate isInclusive() { inclusive = true }
|
||||
|
||||
final override predicate isExclusive() { inclusive = false }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method name literal. For example:
|
||||
* ```rb
|
||||
@@ -1128,25 +820,3 @@ class MethodName extends Literal {
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "MethodName" }
|
||||
}
|
||||
|
||||
private string getMethodName(MethodName::Token t) {
|
||||
result = t.(Ruby::Token).getValue()
|
||||
or
|
||||
result = t.(Ruby::Setter).getName().getValue() + "="
|
||||
}
|
||||
|
||||
private class RequiredMethodNameConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = getMethodName(_) }
|
||||
}
|
||||
|
||||
private class TokenMethodName extends MethodName, TTokenMethodName {
|
||||
private MethodName::Token g;
|
||||
|
||||
TokenMethodName() { this = TTokenMethodName(g) }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(getMethodName(g))
|
||||
}
|
||||
|
||||
final override string toString() { result = getMethodName(g) }
|
||||
}
|
||||
|
||||
364
ruby/ql/lib/codeql/ruby/ast/internal/Literal.qll
Normal file
364
ruby/ql/lib/codeql/ruby/ast/internal/Literal.qll
Normal file
@@ -0,0 +1,364 @@
|
||||
private import codeql.ruby.AST
|
||||
private import AST
|
||||
private import Constant
|
||||
private import TreeSitter
|
||||
private import codeql.ruby.controlflow.CfgNodes
|
||||
|
||||
int parseInteger(Ruby::Integer i) {
|
||||
exists(string s | s = i.getValue().toLowerCase().replaceAll("_", "") |
|
||||
s.charAt(0) != "0" and
|
||||
result = s.toInt()
|
||||
or
|
||||
exists(string str, string values, int shift |
|
||||
s.matches("0b%") and
|
||||
values = "01" and
|
||||
str = s.suffix(2) and
|
||||
shift = 1
|
||||
or
|
||||
s.matches("0x%") and
|
||||
values = "0123456789abcdef" and
|
||||
str = s.suffix(2) and
|
||||
shift = 4
|
||||
or
|
||||
s.charAt(0) = "0" and
|
||||
not s.charAt(1) = ["b", "x", "o"] and
|
||||
values = "01234567" and
|
||||
str = s.suffix(1) and
|
||||
shift = 3
|
||||
or
|
||||
s.matches("0o%") and
|
||||
values = "01234567" and
|
||||
str = s.suffix(2) and
|
||||
shift = 3
|
||||
|
|
||||
result =
|
||||
sum(int index, string c, int v, int exp |
|
||||
c = str.charAt(index) and
|
||||
v = values.indexOf(c.toLowerCase()) and
|
||||
exp = str.length() - index - 1
|
||||
|
|
||||
v.bitShiftLeft((str.length() - index - 1) * shift)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredIntegerLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredInt(int i) { i = any(IntegerLiteral il).getValue() }
|
||||
}
|
||||
|
||||
abstract class IntegerLiteralImpl extends Expr, TIntegerLiteral {
|
||||
abstract int getValueImpl();
|
||||
}
|
||||
|
||||
class IntegerLiteralReal extends IntegerLiteralImpl, TIntegerLiteralReal {
|
||||
private Ruby::Integer g;
|
||||
|
||||
IntegerLiteralReal() { this = TIntegerLiteralReal(g) }
|
||||
|
||||
final override int getValueImpl() { result = parseInteger(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
class IntegerLiteralSynth extends IntegerLiteralImpl, TIntegerLiteralSynth {
|
||||
private int value;
|
||||
|
||||
IntegerLiteralSynth() { this = TIntegerLiteralSynth(_, _, value) }
|
||||
|
||||
final override int getValueImpl() { result = value }
|
||||
|
||||
final override string toString() { result = value.toString() }
|
||||
}
|
||||
|
||||
// TODO: implement properly
|
||||
float parseFloat(Ruby::Float f) { result = f.getValue().toFloat() }
|
||||
|
||||
private class RequiredFloatConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredFloat(float f) { f = parseFloat(_) }
|
||||
}
|
||||
|
||||
predicate isRationalValue(Ruby::Rational r, int numerator, int denominator) {
|
||||
numerator = parseInteger(r.getChild()) and
|
||||
denominator = 1
|
||||
or
|
||||
exists(Ruby::Float f, string regex, string before, string after |
|
||||
f = r.getChild() and
|
||||
regex = "([^.]*)\\.(.*)" and
|
||||
before = f.getValue().regexpCapture(regex, 1) and
|
||||
after = f.getValue().regexpCapture(regex, 2) and
|
||||
numerator = before.toInt() * denominator + after.toInt() and
|
||||
denominator = 10.pow(after.length())
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredRationalConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredRational(int numerator, int denominator) {
|
||||
isRationalValue(_, numerator, denominator)
|
||||
}
|
||||
}
|
||||
|
||||
float getComplexValue(Ruby::Complex c) {
|
||||
exists(string s |
|
||||
s = c.getValue() and
|
||||
result = s.prefix(s.length() - 1).toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredComplexConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredComplex(float real, float imaginary) {
|
||||
real = 0 and
|
||||
imaginary = getComplexValue(_)
|
||||
}
|
||||
}
|
||||
|
||||
class TrueLiteral extends BooleanLiteral, TTrueLiteral {
|
||||
private Ruby::True g;
|
||||
|
||||
TrueLiteral() { this = TTrueLiteral(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override predicate isTrue() { any() }
|
||||
}
|
||||
|
||||
class FalseLiteral extends BooleanLiteral, TFalseLiteral {
|
||||
private Ruby::False g;
|
||||
|
||||
FalseLiteral() { this = TFalseLiteral(g) }
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
|
||||
final override predicate isFalse() { any() }
|
||||
}
|
||||
|
||||
private class RequiredEncodingLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = "UTF-8" }
|
||||
}
|
||||
|
||||
private class RequiredLineLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredInt(int i) { i = any(LineLiteral ll).getLocation().getStartLine() }
|
||||
}
|
||||
|
||||
private class RequiredFileLiteralConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) {
|
||||
s = any(FileLiteral fl).getLocation().getFile().getAbsolutePath()
|
||||
}
|
||||
}
|
||||
|
||||
private class RequiredStringTextComponentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) {
|
||||
s = any(Ruby::Token t | exists(TStringTextComponentNonRegexp(t))).getValue()
|
||||
}
|
||||
}
|
||||
|
||||
private class RequiredStringEscapeSequenceComponentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) {
|
||||
s = any(Ruby::Token t | exists(TStringEscapeSequenceComponentNonRegexp(t))).getValue()
|
||||
}
|
||||
}
|
||||
|
||||
class TRegExpComponent =
|
||||
TStringTextComponentRegexp or TStringEscapeSequenceComponentRegexp or
|
||||
TStringInterpolationComponentRegexp;
|
||||
|
||||
// Exclude components that are children of a free-spacing regex.
|
||||
// We do this because `ParseRegExp.qll` cannot handle free-spacing regexes.
|
||||
string getRegExpTextComponentValue(RegExpTextComponent c) {
|
||||
exists(Ruby::Token t |
|
||||
c = TStringTextComponentRegexp(t) and
|
||||
not c.getParent().(RegExpLiteral).hasFreeSpacingFlag() and
|
||||
result = t.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredRegExpTextComponentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = getRegExpTextComponentValue(_) }
|
||||
}
|
||||
|
||||
// Exclude components that are children of a free-spacing regex.
|
||||
// We do this because `ParseRegExp.qll` cannot handle free-spacing regexes.
|
||||
string getRegExpEscapeSequenceComponentValue(RegExpEscapeSequenceComponent c) {
|
||||
exists(Ruby::EscapeSequence e |
|
||||
c = TStringEscapeSequenceComponentRegexp(e) and
|
||||
not c.getParent().(RegExpLiteral).hasFreeSpacingFlag() and
|
||||
result = e.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
private class RequiredRegExpEscapeSequenceComponentConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = getRegExpEscapeSequenceComponentValue(_) }
|
||||
}
|
||||
|
||||
class RegularStringLiteral extends StringLiteral, TRegularStringLiteral {
|
||||
private Ruby::String g;
|
||||
|
||||
RegularStringLiteral() { this = TRegularStringLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) }
|
||||
}
|
||||
|
||||
class BareStringLiteral extends StringLiteral, TBareStringLiteral {
|
||||
private Ruby::BareString g;
|
||||
|
||||
BareStringLiteral() { this = TBareStringLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) }
|
||||
}
|
||||
|
||||
// Tree-sitter gives us value text including the colon, which we skip.
|
||||
string getSimpleSymbolValue(Ruby::SimpleSymbol ss) { result = ss.getValue().suffix(1) }
|
||||
|
||||
private class RequiredSimpleSymbolConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredSymbol(string s) { s = getSimpleSymbolValue(_) }
|
||||
}
|
||||
|
||||
private class SimpleSymbolLiteral extends SymbolLiteral, TSimpleSymbolLiteral {
|
||||
private Ruby::SimpleSymbol g;
|
||||
|
||||
SimpleSymbolLiteral() { this = TSimpleSymbolLiteral(g) }
|
||||
|
||||
final override ConstantValue::ConstantSymbolValue getConstantValue() {
|
||||
result.isSymbol(getSimpleSymbolValue(g))
|
||||
}
|
||||
|
||||
final override string toString() { result = g.getValue() }
|
||||
}
|
||||
|
||||
class ComplexSymbolLiteral extends SymbolLiteral, TComplexSymbolLiteral { }
|
||||
|
||||
class DelimitedSymbolLiteral extends ComplexSymbolLiteral, TDelimitedSymbolLiteral {
|
||||
private Ruby::DelimitedSymbol g;
|
||||
|
||||
DelimitedSymbolLiteral() { this = TDelimitedSymbolLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) }
|
||||
}
|
||||
|
||||
class BareSymbolLiteral extends ComplexSymbolLiteral, TBareSymbolLiteral {
|
||||
private Ruby::BareSymbol g;
|
||||
|
||||
BareSymbolLiteral() { this = TBareSymbolLiteral(g) }
|
||||
|
||||
final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) }
|
||||
}
|
||||
|
||||
private class RequiredHashKeySymbolConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredSymbol(string s) { s = any(Ruby::HashKeySymbol h).getValue() }
|
||||
}
|
||||
|
||||
private class HashKeySymbolLiteral extends SymbolLiteral, THashKeySymbolLiteral {
|
||||
private Ruby::HashKeySymbol g;
|
||||
|
||||
HashKeySymbolLiteral() { this = THashKeySymbolLiteral(g) }
|
||||
|
||||
final override ConstantValue::ConstantSymbolValue getConstantValue() {
|
||||
result.isSymbol(g.getValue())
|
||||
}
|
||||
|
||||
final override string toString() { result = ":" + g.getValue() }
|
||||
}
|
||||
|
||||
private class RequiredCharacterConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = any(Ruby::Character c).getValue() }
|
||||
}
|
||||
|
||||
abstract class ArrayLiteralImpl extends Literal, TArrayLiteral {
|
||||
abstract Expr getElementImpl(int n);
|
||||
|
||||
abstract int getNumberOfElementsImpl();
|
||||
}
|
||||
|
||||
class RegularArrayLiteral extends ArrayLiteralImpl, TRegularArrayLiteral {
|
||||
private Ruby::Array g;
|
||||
|
||||
RegularArrayLiteral() { this = TRegularArrayLiteral(g) }
|
||||
|
||||
final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) }
|
||||
|
||||
final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) }
|
||||
|
||||
final override string toString() { result = "[...]" }
|
||||
}
|
||||
|
||||
class StringArrayLiteral extends ArrayLiteralImpl, TStringArrayLiteral {
|
||||
private Ruby::StringArray g;
|
||||
|
||||
StringArrayLiteral() { this = TStringArrayLiteral(g) }
|
||||
|
||||
final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) }
|
||||
|
||||
final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) }
|
||||
|
||||
final override string toString() { result = "%w(...)" }
|
||||
}
|
||||
|
||||
class SymbolArrayLiteral extends ArrayLiteralImpl, TSymbolArrayLiteral {
|
||||
private Ruby::SymbolArray g;
|
||||
|
||||
SymbolArrayLiteral() { this = TSymbolArrayLiteral(g) }
|
||||
|
||||
final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) }
|
||||
|
||||
final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) }
|
||||
|
||||
final override string toString() { result = "%i(...)" }
|
||||
}
|
||||
|
||||
class HashLiteralImpl extends Literal, THashLiteral {
|
||||
Ruby::Hash g;
|
||||
|
||||
HashLiteralImpl() { this = THashLiteral(g) }
|
||||
|
||||
final int getNumberOfElementsImpl() { result = count(g.getChild(_)) }
|
||||
}
|
||||
|
||||
class RangeLiteralReal extends RangeLiteral, TRangeLiteralReal {
|
||||
private Ruby::Range g;
|
||||
|
||||
RangeLiteralReal() { this = TRangeLiteralReal(g) }
|
||||
|
||||
final override Expr getBegin() { toGenerated(result) = g.getBegin() }
|
||||
|
||||
final override Expr getEnd() { toGenerated(result) = g.getEnd() }
|
||||
|
||||
final override predicate isInclusive() { g instanceof @ruby_range_dotdot }
|
||||
|
||||
final override predicate isExclusive() { g instanceof @ruby_range_dotdotdot }
|
||||
}
|
||||
|
||||
class RangeLiteralSynth extends RangeLiteral, TRangeLiteralSynth {
|
||||
private boolean inclusive;
|
||||
|
||||
RangeLiteralSynth() { this = TRangeLiteralSynth(_, _, inclusive) }
|
||||
|
||||
final override Expr getBegin() { result = TIntegerLiteralSynth(this, 0, _) }
|
||||
|
||||
final override Expr getEnd() { result = TIntegerLiteralSynth(this, 1, _) }
|
||||
|
||||
final override predicate isInclusive() { inclusive = true }
|
||||
|
||||
final override predicate isExclusive() { inclusive = false }
|
||||
}
|
||||
|
||||
private string getMethodName(MethodName::Token t) {
|
||||
result = t.(Ruby::Token).getValue()
|
||||
or
|
||||
result = t.(Ruby::Setter).getName().getValue() + "="
|
||||
}
|
||||
|
||||
private class RequiredMethodNameConstantValue extends RequiredConstantValue {
|
||||
override predicate requiredString(string s) { s = getMethodName(_) }
|
||||
}
|
||||
|
||||
class TokenMethodName extends MethodName, TTokenMethodName {
|
||||
private MethodName::Token g;
|
||||
|
||||
TokenMethodName() { this = TTokenMethodName(g) }
|
||||
|
||||
final override ConstantValue::ConstantStringValue getConstantValue() {
|
||||
result.isString(getMethodName(g))
|
||||
}
|
||||
|
||||
final override string toString() { result = getMethodName(g) }
|
||||
}
|
||||
@@ -794,14 +794,13 @@ private module ArrayLiteralDesugar {
|
||||
exists(ArrayLiteral al |
|
||||
parent = al and
|
||||
i = -1 and
|
||||
child = SynthChild(MethodCallKind("[]", false, al.getNumberOfElements() + 1))
|
||||
child = SynthChild(MethodCallKind("[]", false, al.getNumberOfElements()))
|
||||
or
|
||||
exists(AstNode mc | mc = TMethodCallSynth(al, -1, _, _, _) |
|
||||
parent = mc and
|
||||
parent = TMethodCallSynth(al, -1, _, _, _) and
|
||||
(
|
||||
i = 0 and
|
||||
child = SynthChild(ConstantReadAccessKind("::Array"))
|
||||
or
|
||||
parent = mc and
|
||||
child = childRef(al.getElement(i - 1))
|
||||
)
|
||||
)
|
||||
@@ -825,13 +824,56 @@ private module ArrayLiteralDesugar {
|
||||
final override predicate methodCall(string name, boolean setter, int arity) {
|
||||
name = "[]" and
|
||||
setter = false and
|
||||
arity = any(ArrayLiteral al).getNumberOfElements() + 1
|
||||
arity = any(ArrayLiteral al).getNumberOfElements()
|
||||
}
|
||||
|
||||
final override predicate constantReadAccess(string name) { name = "::Array" }
|
||||
}
|
||||
}
|
||||
|
||||
private module HashLiteralDesugar {
|
||||
pragma[nomagic]
|
||||
private predicate hashLiteralSynthesis(AstNode parent, int i, Child child) {
|
||||
exists(HashLiteral hl |
|
||||
parent = hl and
|
||||
i = -1 and
|
||||
child = SynthChild(MethodCallKind("[]", false, hl.getNumberOfElements()))
|
||||
or
|
||||
parent = TMethodCallSynth(hl, -1, _, _, _) and
|
||||
(
|
||||
i = 0 and
|
||||
child = SynthChild(ConstantReadAccessKind("::Hash"))
|
||||
or
|
||||
child = childRef(hl.getElement(i - 1))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* ```rb
|
||||
* { a: 1, **splat, b: 2 }
|
||||
* ```
|
||||
* desugars to
|
||||
*
|
||||
* ```rb
|
||||
* ::Hash.[](a: 1, **splat, b: 2)
|
||||
* ```
|
||||
*/
|
||||
private class HashLiteralSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
hashLiteralSynthesis(parent, i, child)
|
||||
}
|
||||
|
||||
final override predicate methodCall(string name, boolean setter, int arity) {
|
||||
name = "[]" and
|
||||
setter = false and
|
||||
arity = any(HashLiteral hl).getNumberOfElements()
|
||||
}
|
||||
|
||||
final override predicate constantReadAccess(string name) { name = "::Hash" }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ```rb
|
||||
* for x in xs
|
||||
|
||||
@@ -484,6 +484,9 @@ module ExprNodes {
|
||||
/** Gets the `n`th argument of this call. */
|
||||
final ExprCfgNode getArgument(int n) { e.hasCfgChild(e.getArgument(n), this, result) }
|
||||
|
||||
/** Gets an argument of this call. */
|
||||
final ExprCfgNode getAnArgument() { result = this.getArgument(_) }
|
||||
|
||||
/** Gets the the keyword argument whose key is `keyword` of this call. */
|
||||
final ExprCfgNode getKeywordArgument(string keyword) {
|
||||
exists(PairCfgNode n |
|
||||
@@ -940,4 +943,39 @@ module ExprNodes {
|
||||
|
||||
final override ElementReference getExpr() { result = super.getExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A control-flow node that wraps an array literal. Array literals are desugared
|
||||
* into calls to `Array.[]`, so this includes both desugared calls as well as
|
||||
* explicit calls.
|
||||
*/
|
||||
class ArrayLiteralCfgNode extends MethodCallCfgNode {
|
||||
ArrayLiteralCfgNode() {
|
||||
exists(ConstantReadAccess array |
|
||||
array = this.getReceiver().getExpr() and
|
||||
e.(MethodCall).getMethodName() = "[]" and
|
||||
array.getName() = "Array" and
|
||||
array.hasGlobalScope()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A control-flow node that wraps a hash literal. Hash literals are desugared
|
||||
* into calls to `Hash.[]`, so this includes both desugared calls as well as
|
||||
* explicit calls.
|
||||
*/
|
||||
class HashLiteralCfgNode extends MethodCallCfgNode {
|
||||
HashLiteralCfgNode() {
|
||||
exists(ConstantReadAccess array |
|
||||
array = this.getReceiver().getExpr() and
|
||||
e.(MethodCall).getMethodName() = "[]" and
|
||||
array.getName() = "Hash" and
|
||||
array.hasGlobalScope()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a pair of this hash literal. */
|
||||
PairCfgNode getAKeyValuePair() { result = this.getAnArgument() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -986,10 +986,6 @@ module Trees {
|
||||
|
||||
private class GlobalVariableTree extends LeafTree, GlobalVariableAccess { }
|
||||
|
||||
private class HashLiteralTree extends StandardPostOrderTree, HashLiteral {
|
||||
final override ControlFlowTree getChildElement(int i) { result = this.getElement(i) }
|
||||
}
|
||||
|
||||
private class HashSplatNilParameterTree extends LeafTree, HashSplatNilParameter { }
|
||||
|
||||
private class HashSplatParameterTree extends NonDefaultValueParameterTree, HashSplatParameter { }
|
||||
|
||||
@@ -278,16 +278,9 @@ private DataFlow::LocalSourceNode trackInstance(Module tp, TypeTracker t) {
|
||||
or
|
||||
result.asExpr().getExpr() instanceof StringlikeLiteral and tp = TResolved("String")
|
||||
or
|
||||
exists(ConstantReadAccess array, MethodCall mc |
|
||||
result.asExpr().getExpr() = mc and
|
||||
mc.getMethodName() = "[]" and
|
||||
mc.getReceiver() = array and
|
||||
array.getName() = "Array" and
|
||||
array.hasGlobalScope() and
|
||||
tp = TResolved("Array")
|
||||
)
|
||||
result.asExpr() instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode and tp = TResolved("Array")
|
||||
or
|
||||
result.asExpr().getExpr() instanceof HashLiteral and tp = TResolved("Hash")
|
||||
result.asExpr() instanceof CfgNodes::ExprNodes::HashLiteralCfgNode and tp = TResolved("Hash")
|
||||
or
|
||||
result.asExpr().getExpr() instanceof MethodBase and tp = TResolved("Symbol")
|
||||
or
|
||||
|
||||
@@ -52,14 +52,15 @@ private class LibXmlRubyXmlParserCall extends XmlParserCall::Range, DataFlow::Ca
|
||||
override DataFlow::Node getInput() { result = this.getArgument(0) }
|
||||
|
||||
override predicate externalEntitiesEnabled() {
|
||||
exists(Pair pair |
|
||||
pair = this.getArgument(1).asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(CfgNodes::ExprNodes::PairCfgNode pair |
|
||||
pair =
|
||||
this.getArgument(1).asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
pair.getKey().getConstantValue().isStringOrSymbol("options") and
|
||||
pair.getValue() =
|
||||
[
|
||||
trackEnableFeature(TNOENT()), trackEnableFeature(TDTDLOAD()),
|
||||
trackDisableFeature(TNONET())
|
||||
].asExpr().getExpr()
|
||||
].asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
@@ -91,16 +92,16 @@ class ExconHttpRequest extends HTTP::Client::Request::Range {
|
||||
predicate argSetsVerifyPeer(DataFlow::Node arg, boolean value, DataFlow::Node kvNode) {
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// Excon.get(..., ssl_verify_peer: false)
|
||||
isSslVerifyPeerPair(arg.asExpr().getExpr(), value) and
|
||||
isSslVerifyPeerPair(arg.asExpr(), value) and
|
||||
kvNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// Excon.get(..., { ssl_verify_peer: false, ... })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isSslVerifyPeerPair(p, value) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
kvNode.asExpr().getExpr() = p
|
||||
kvNode.asExpr() = p
|
||||
)
|
||||
}
|
||||
|
||||
@@ -133,10 +134,10 @@ private predicate hasBooleanValue(DataFlow::Node node, boolean value) {
|
||||
}
|
||||
|
||||
/** Holds if `p` is the pair `ssl_verify_peer: <value>`. */
|
||||
private predicate isSslVerifyPeerPair(Pair p, boolean value) {
|
||||
private predicate isSslVerifyPeerPair(CfgNodes::ExprNodes::PairCfgNode p, boolean value) {
|
||||
exists(DataFlow::Node key, DataFlow::Node valueNode |
|
||||
key.asExpr().getExpr() = p.getKey() and valueNode.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
valueNode.asExpr() = p.getValue() and
|
||||
isSslVerifyPeerLiteral(key) and
|
||||
hasBooleanValue(valueNode, value)
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
@@ -56,16 +57,16 @@ class FaradayHttpRequest extends HTTP::Client::Request::Range {
|
||||
|
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// Faraday.new(..., ssl: {...})
|
||||
isSslOptionsPairDisablingValidation(arg.asExpr().getExpr()) and
|
||||
isSslOptionsPairDisablingValidation(arg.asExpr()) and
|
||||
disablingNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// Faraday.new(..., { ssl: {...} })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isSslOptionsPairDisablingValidation(p) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
disablingNode.asExpr().getExpr() = p
|
||||
disablingNode.asExpr() = p
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -78,10 +79,10 @@ class FaradayHttpRequest extends HTTP::Client::Request::Range {
|
||||
* containing either `verify: false` or
|
||||
* `verify_mode: OpenSSL::SSL::VERIFY_NONE`.
|
||||
*/
|
||||
private predicate isSslOptionsPairDisablingValidation(Pair p) {
|
||||
private predicate isSslOptionsPairDisablingValidation(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSymbolLiteral(key, "ssl") and
|
||||
(isHashWithVerifyFalse(value) or isHashWithVerifyModeNone(value))
|
||||
)
|
||||
@@ -101,7 +102,7 @@ private predicate isSymbolLiteral(DataFlow::Node node, string valueText) {
|
||||
*/
|
||||
private predicate isHashWithVerifyFalse(DataFlow::Node node) {
|
||||
exists(DataFlow::LocalSourceNode hash |
|
||||
isVerifyFalsePair(hash.asExpr().getExpr().(HashLiteral).getAKeyValuePair()) and
|
||||
isVerifyFalsePair(hash.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair()) and
|
||||
hash.flowsTo(node)
|
||||
)
|
||||
}
|
||||
@@ -112,7 +113,7 @@ private predicate isHashWithVerifyFalse(DataFlow::Node node) {
|
||||
*/
|
||||
private predicate isHashWithVerifyModeNone(DataFlow::Node node) {
|
||||
exists(DataFlow::LocalSourceNode hash |
|
||||
isVerifyModeNonePair(hash.asExpr().getExpr().(HashLiteral).getAKeyValuePair()) and
|
||||
isVerifyModeNonePair(hash.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair()) and
|
||||
hash.flowsTo(node)
|
||||
)
|
||||
}
|
||||
@@ -121,10 +122,10 @@ private predicate isHashWithVerifyModeNone(DataFlow::Node node) {
|
||||
* Holds if the pair `p` has the key `:verify_mode` and the value
|
||||
* `OpenSSL::SSL::VERIFY_NONE`.
|
||||
*/
|
||||
private predicate isVerifyModeNonePair(Pair p) {
|
||||
private predicate isVerifyModeNonePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSymbolLiteral(key, "verify_mode") and
|
||||
value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse()
|
||||
)
|
||||
@@ -133,10 +134,10 @@ private predicate isVerifyModeNonePair(Pair p) {
|
||||
/**
|
||||
* Holds if the pair `p` has the key `:verify` and the value `false`.
|
||||
*/
|
||||
private predicate isVerifyFalsePair(Pair p) {
|
||||
private predicate isVerifyFalsePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSymbolLiteral(key, "verify") and
|
||||
isFalse(value)
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
@@ -48,20 +49,21 @@ class HttpartyRequest extends HTTP::Client::Request::Range {
|
||||
// argument, and we're looking for `{ verify: false }` or
|
||||
// `{ verify_peer: false }`.
|
||||
exists(DataFlow::Node arg, int i |
|
||||
i > 0 and arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(i)
|
||||
i > 0 and
|
||||
arg.asExpr() = requestUse.asExpr().(CfgNodes::ExprNodes::MethodCallCfgNode).getArgument(i)
|
||||
|
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// HTTParty.get(..., verify: false)
|
||||
isVerifyFalsePair(arg.asExpr().getExpr()) and
|
||||
isVerifyFalsePair(arg.asExpr()) and
|
||||
disablingNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// HTTParty.get(..., { verify: false, ... })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isVerifyFalsePair(p) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
disablingNode.asExpr().getExpr() = p
|
||||
disablingNode.asExpr() = p
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -88,10 +90,10 @@ private predicate isFalse(DataFlow::Node node) {
|
||||
/**
|
||||
* Holds if `p` is the pair `verify: false` or `verify_peer: false`.
|
||||
*/
|
||||
private predicate isVerifyFalsePair(Pair p) {
|
||||
private predicate isVerifyFalsePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isVerifyLiteral(key) and
|
||||
isFalse(value)
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.frameworks.StandardLibrary
|
||||
@@ -32,8 +33,7 @@ class OpenUriRequest extends HTTP::Client::Request::Range {
|
||||
|
||||
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {
|
||||
exists(DataFlow::Node arg |
|
||||
arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(_)
|
||||
|
|
||||
arg.asExpr() = requestUse.asExpr().(CfgNodes::ExprNodes::MethodCallCfgNode).getAnArgument() and
|
||||
argumentDisablesValidation(arg, disablingNode)
|
||||
)
|
||||
}
|
||||
@@ -68,8 +68,7 @@ class OpenUriKernelOpenRequest extends HTTP::Client::Request::Range {
|
||||
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {
|
||||
exists(DataFlow::Node arg, int i |
|
||||
i > 0 and
|
||||
arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(i)
|
||||
|
|
||||
arg.asExpr() = requestUse.asExpr().(CfgNodes::ExprNodes::MethodCallCfgNode).getArgument(i) and
|
||||
argumentDisablesValidation(arg, disablingNode)
|
||||
)
|
||||
}
|
||||
@@ -85,24 +84,24 @@ class OpenUriKernelOpenRequest extends HTTP::Client::Request::Range {
|
||||
private predicate argumentDisablesValidation(DataFlow::Node arg, DataFlow::Node disablingNode) {
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// URI.open(..., ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
||||
isSslVerifyModeNonePair(arg.asExpr().getExpr()) and
|
||||
isSslVerifyModeNonePair(arg.asExpr()) and
|
||||
disablingNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// URI.open(..., { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE, ... })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isSslVerifyModeNonePair(p) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
disablingNode.asExpr().getExpr() = p
|
||||
disablingNode.asExpr() = p
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `p` is the pair `ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE`. */
|
||||
private predicate isSslVerifyModeNonePair(Pair p) {
|
||||
private predicate isSslVerifyModeNonePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSslVerifyModeLiteral(key) and
|
||||
value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse()
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
@@ -50,16 +51,16 @@ class RestClientHttpRequest extends HTTP::Client::Request::Range {
|
||||
|
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// RestClient::Resource.new(..., verify_ssl: OpenSSL::SSL::VERIFY_NONE)
|
||||
isVerifySslNonePair(arg.asExpr().getExpr()) and
|
||||
isVerifySslNonePair(arg.asExpr()) and
|
||||
disablingNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// RestClient::Resource.new(..., { verify_ssl: OpenSSL::SSL::VERIFY_NONE })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isVerifySslNonePair(p) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
disablingNode.asExpr().getExpr() = p
|
||||
disablingNode.asExpr() = p
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -68,10 +69,10 @@ class RestClientHttpRequest extends HTTP::Client::Request::Range {
|
||||
}
|
||||
|
||||
/** Holds if `p` is the pair `verify_ssl: OpenSSL::SSL::VERIFY_NONE`. */
|
||||
private predicate isVerifySslNonePair(Pair p) {
|
||||
private predicate isVerifySslNonePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSslVerifyModeLiteral(key) and
|
||||
value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse()
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.CFG
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
@@ -31,16 +32,16 @@ class TyphoeusHttpRequest extends HTTP::Client::Request::Range {
|
||||
|
|
||||
// Either passed as an individual key:value argument, e.g.:
|
||||
// Typhoeus.get(..., ssl_verifypeer: false)
|
||||
isSslVerifyPeerFalsePair(arg.asExpr().getExpr()) and
|
||||
isSslVerifyPeerFalsePair(arg.asExpr()) and
|
||||
disablingNode = arg
|
||||
or
|
||||
// Or as a single hash argument, e.g.:
|
||||
// Typhoeus.get(..., { ssl_verifypeer: false, ... })
|
||||
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
|
||||
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
|
||||
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
|
||||
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
|
||||
isSslVerifyPeerFalsePair(p) and
|
||||
optionsNode.flowsTo(arg) and
|
||||
disablingNode.asExpr().getExpr() = p
|
||||
disablingNode.asExpr() = p
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -49,11 +50,10 @@ class TyphoeusHttpRequest extends HTTP::Client::Request::Range {
|
||||
}
|
||||
|
||||
/** Holds if `p` is the pair `ssl_verifypeer: false`. */
|
||||
private predicate isSslVerifyPeerFalsePair(Pair p) {
|
||||
private predicate isSslVerifyPeerFalsePair(CfgNodes::ExprNodes::PairCfgNode p) {
|
||||
exists(DataFlow::Node key, DataFlow::Node value |
|
||||
key.asExpr().getExpr() = p.getKey() and
|
||||
value.asExpr().getExpr() = p.getValue()
|
||||
|
|
||||
key.asExpr() = p.getKey() and
|
||||
value.asExpr() = p.getValue() and
|
||||
isSslVerifyPeerLiteral(key) and
|
||||
isFalse(value)
|
||||
)
|
||||
|
||||
@@ -73,12 +73,12 @@ module UnsafeDeserialization {
|
||||
result = "object" and isSafe = false
|
||||
}
|
||||
|
||||
private predicate isOjModePair(Pair p, string modeValue) {
|
||||
private predicate isOjModePair(CfgNodes::ExprNodes::PairCfgNode p, string modeValue) {
|
||||
p.getKey().getConstantValue().isStringOrSymbol("mode") and
|
||||
exists(DataFlow::LocalSourceNode symbolLiteral, DataFlow::Node value |
|
||||
symbolLiteral.asExpr().getExpr().getConstantValue().isSymbol(modeValue) and
|
||||
symbolLiteral.flowsTo(value) and
|
||||
value.asExpr().getExpr() = p.getValue()
|
||||
value.asExpr() = p.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,7 +91,8 @@ module UnsafeDeserialization {
|
||||
OjOptionsHashWithModeKey() {
|
||||
exists(DataFlow::LocalSourceNode options |
|
||||
options.flowsTo(this) and
|
||||
isOjModePair(options.asExpr().getExpr().(HashLiteral).getAKeyValuePair(), modeValue)
|
||||
isOjModePair(options.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair(),
|
||||
modeValue)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -143,7 +144,7 @@ module UnsafeDeserialization {
|
||||
exists(DataFlow::Node arg, int i | i >= 1 and arg = this.getArgument(i) |
|
||||
arg.(OjOptionsHashWithModeKey).hasKnownMode(isSafe)
|
||||
or
|
||||
isOjModePair(arg.asExpr().getExpr(), getAKnownOjModeName(isSafe))
|
||||
isOjModePair(arg.asExpr(), getAKnownOjModeName(isSafe))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,19 @@ calls/calls.rb:
|
||||
# 230| getReceiver: [ConstantReadAccess] X
|
||||
# 229| getReceiver: [MethodCall] call to bar
|
||||
# 229| getReceiver: [ConstantReadAccess] X
|
||||
# 249| [HashLiteral] {...}
|
||||
# 249| getDesugared: [MethodCall] call to []
|
||||
# 249| getReceiver: [ConstantReadAccess] Hash
|
||||
# 249| getArgument: [Pair] Pair
|
||||
# 249| getKey: [MethodCall] call to foo
|
||||
# 249| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 249| getValue: [MethodCall] call to bar
|
||||
# 249| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 249| getArgument: [Pair] Pair
|
||||
# 249| getKey: [MethodCall] call to foo
|
||||
# 249| getReceiver: [ConstantReadAccess] X
|
||||
# 249| getValue: [MethodCall] call to bar
|
||||
# 249| getReceiver: [ConstantReadAccess] X
|
||||
# 314| [AssignExpr] ... = ...
|
||||
# 314| getDesugared: [StmtSequence] ...
|
||||
# 314| getStmt: [SetterMethodCall] call to foo=
|
||||
@@ -292,6 +305,13 @@ constants/constants.rb:
|
||||
# 20| getArgument: [StringLiteral] "Dave"
|
||||
# 20| getComponent: [StringTextComponent] Dave
|
||||
literals/literals.rb:
|
||||
# 88| [HashLiteral] {...}
|
||||
# 88| getDesugared: [MethodCall] call to []
|
||||
# 88| getReceiver: [ConstantReadAccess] Hash
|
||||
# 88| getArgument: [Pair] Pair
|
||||
# 88| getKey: [SymbolLiteral] :foo
|
||||
# 88| getValue: [StringLiteral] "bar"
|
||||
# 88| getComponent: [StringTextComponent] bar
|
||||
# 96| [ArrayLiteral] [...]
|
||||
# 96| getDesugared: [MethodCall] call to []
|
||||
# 96| getReceiver: [ConstantReadAccess] Array
|
||||
@@ -432,6 +452,31 @@ literals/literals.rb:
|
||||
# 113| getComponent: [StringTextComponent] #{BAR}
|
||||
# 113| getArgument: [SymbolLiteral] :"baz"
|
||||
# 113| getComponent: [StringTextComponent] baz
|
||||
# 116| [HashLiteral] {...}
|
||||
# 116| getDesugared: [MethodCall] call to []
|
||||
# 116| getReceiver: [ConstantReadAccess] Hash
|
||||
# 117| [HashLiteral] {...}
|
||||
# 117| getDesugared: [MethodCall] call to []
|
||||
# 117| getReceiver: [ConstantReadAccess] Hash
|
||||
# 117| getArgument: [Pair] Pair
|
||||
# 117| getKey: [SymbolLiteral] :foo
|
||||
# 117| getValue: [IntegerLiteral] 1
|
||||
# 117| getArgument: [Pair] Pair
|
||||
# 117| getKey: [SymbolLiteral] :bar
|
||||
# 117| getValue: [IntegerLiteral] 2
|
||||
# 117| getArgument: [Pair] Pair
|
||||
# 117| getKey: [StringLiteral] "baz"
|
||||
# 117| getComponent: [StringTextComponent] baz
|
||||
# 117| getValue: [IntegerLiteral] 3
|
||||
# 118| [HashLiteral] {...}
|
||||
# 118| getDesugared: [MethodCall] call to []
|
||||
# 118| getReceiver: [ConstantReadAccess] Hash
|
||||
# 118| getArgument: [Pair] Pair
|
||||
# 118| getKey: [SymbolLiteral] :foo
|
||||
# 118| getValue: [IntegerLiteral] 7
|
||||
# 118| getArgument: [HashSplatExpr] ** ...
|
||||
# 118| getAnOperand/getOperand/getReceiver: [MethodCall] call to baz
|
||||
# 118| getReceiver: [Self, SelfVariableAccess] self
|
||||
control/loops.rb:
|
||||
# 9| [ForExpr] for ... in ...
|
||||
# 9| getDesugared: [MethodCall] call to each
|
||||
@@ -511,12 +556,14 @@ control/loops.rb:
|
||||
# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
|
||||
# 24| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 22| getReceiver: [HashLiteral] {...}
|
||||
# 22| getElement: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :foo
|
||||
# 22| getValue: [IntegerLiteral] 0
|
||||
# 22| getElement: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :bar
|
||||
# 22| getValue: [IntegerLiteral] 1
|
||||
# 22| getDesugared: [MethodCall] call to []
|
||||
# 22| getReceiver: [ConstantReadAccess] Hash
|
||||
# 22| getArgument: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :foo
|
||||
# 22| getValue: [IntegerLiteral] 0
|
||||
# 22| getArgument: [Pair] Pair
|
||||
# 22| getKey: [SymbolLiteral] :bar
|
||||
# 22| getValue: [IntegerLiteral] 1
|
||||
# 28| [ForExpr] for ... in ...
|
||||
# 28| getDesugared: [MethodCall] call to each
|
||||
# 28| getBlock: [BraceBlock] { ... }
|
||||
@@ -553,12 +600,14 @@ control/loops.rb:
|
||||
# 30| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
|
||||
# 31| getStmt: [BreakStmt] break
|
||||
# 28| getReceiver: [HashLiteral] {...}
|
||||
# 28| getElement: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :foo
|
||||
# 28| getValue: [IntegerLiteral] 0
|
||||
# 28| getElement: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :bar
|
||||
# 28| getValue: [IntegerLiteral] 1
|
||||
# 28| getDesugared: [MethodCall] call to []
|
||||
# 28| getReceiver: [ConstantReadAccess] Hash
|
||||
# 28| getArgument: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :foo
|
||||
# 28| getValue: [IntegerLiteral] 0
|
||||
# 28| getArgument: [Pair] Pair
|
||||
# 28| getKey: [SymbolLiteral] :bar
|
||||
# 28| getValue: [IntegerLiteral] 1
|
||||
# 36| [AssignAddExpr] ... += ...
|
||||
# 36| getDesugared: [AssignExpr] ... = ...
|
||||
# 36| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
@@ -624,6 +673,15 @@ operations/operations.rb:
|
||||
# 29| getDesugared: [MethodCall] call to []
|
||||
# 29| getReceiver: [ConstantReadAccess] Array
|
||||
# 29| getArgument: [IntegerLiteral] 2
|
||||
# 29| [HashLiteral] {...}
|
||||
# 29| getDesugared: [MethodCall] call to []
|
||||
# 29| getReceiver: [ConstantReadAccess] Hash
|
||||
# 29| getArgument: [Pair] Pair
|
||||
# 29| getKey: [SymbolLiteral] :b
|
||||
# 29| getValue: [IntegerLiteral] 4
|
||||
# 29| getArgument: [Pair] Pair
|
||||
# 29| getKey: [SymbolLiteral] :c
|
||||
# 29| getValue: [IntegerLiteral] 5
|
||||
# 69| [AssignAddExpr] ... += ...
|
||||
# 69| getDesugared: [AssignExpr] ... = ...
|
||||
# 69| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
@@ -721,6 +779,9 @@ operations/operations.rb:
|
||||
# 96| getAnOperand/getLeftOperand/getReceiver: [GlobalVariableAccess] $global_var
|
||||
# 96| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6
|
||||
params/params.rb:
|
||||
# 8| [HashLiteral] {...}
|
||||
# 8| getDesugared: [MethodCall] call to []
|
||||
# 8| getReceiver: [ConstantReadAccess] Hash
|
||||
# 21| [ArrayLiteral] [...]
|
||||
# 21| getDesugared: [MethodCall] call to []
|
||||
# 21| getReceiver: [ConstantReadAccess] Array
|
||||
|
||||
@@ -11,6 +11,8 @@ hashSplatExpr
|
||||
| calls.rb:274:5:274:9 | ** ... | calls.rb:274:7:274:9 | call to bar |
|
||||
| calls.rb:275:5:275:12 | ** ... | calls.rb:275:7:275:12 | call to bar |
|
||||
keywordArguments
|
||||
| calls.rb:249:3:249:12 | Pair | calls.rb:249:3:249:5 | call to foo | calls.rb:249:10:249:12 | call to bar |
|
||||
| calls.rb:249:15:249:30 | Pair | calls.rb:249:15:249:20 | call to foo | calls.rb:249:25:249:30 | call to bar |
|
||||
| calls.rb:278:5:278:13 | Pair | calls.rb:278:5:278:8 | :blah | calls.rb:278:11:278:13 | call to bar |
|
||||
| calls.rb:279:5:279:16 | Pair | calls.rb:279:5:279:8 | :blah | calls.rb:279:11:279:16 | call to bar |
|
||||
keywordArgumentsByKeyword
|
||||
|
||||
@@ -22,6 +22,8 @@ callsWithArguments
|
||||
| calls.rb:85:1:85:12 | ... + ... | + | 0 | calls.rb:85:7:85:12 | call to bar |
|
||||
| calls.rb:234:1:234:8 | ...[...] | [] | 0 | calls.rb:234:5:234:7 | call to bar |
|
||||
| calls.rb:235:1:235:14 | ...[...] | [] | 0 | calls.rb:235:8:235:13 | call to bar |
|
||||
| calls.rb:249:1:249:32 | call to [] | [] | 0 | calls.rb:249:3:249:12 | Pair |
|
||||
| calls.rb:249:1:249:32 | call to [] | [] | 1 | calls.rb:249:15:249:30 | Pair |
|
||||
| calls.rb:266:1:266:9 | call to foo | foo | 0 | calls.rb:266:5:266:8 | &... |
|
||||
| calls.rb:267:1:267:12 | call to foo | foo | 0 | calls.rb:267:5:267:11 | &... |
|
||||
| calls.rb:270:1:270:9 | call to foo | foo | 0 | calls.rb:270:5:270:8 | * ... |
|
||||
@@ -248,6 +250,7 @@ callsWithReceiver
|
||||
| calls.rb:245:6:245:8 | call to bar | calls.rb:245:6:245:8 | self |
|
||||
| calls.rb:246:1:246:6 | call to foo | calls.rb:246:1:246:1 | X |
|
||||
| calls.rb:246:9:246:14 | call to bar | calls.rb:246:9:246:9 | X |
|
||||
| calls.rb:249:1:249:32 | call to [] | calls.rb:249:1:249:32 | Hash |
|
||||
| calls.rb:249:3:249:5 | call to foo | calls.rb:249:3:249:5 | self |
|
||||
| calls.rb:249:10:249:12 | call to bar | calls.rb:249:10:249:12 | self |
|
||||
| calls.rb:249:15:249:20 | call to foo | calls.rb:249:15:249:15 | X |
|
||||
|
||||
@@ -2434,11 +2434,14 @@ cfg.rb:
|
||||
#-----| -> map2
|
||||
|
||||
# 97| map1
|
||||
#-----| -> a
|
||||
#-----| -> Hash
|
||||
|
||||
# 97| {...}
|
||||
# 97| call to []
|
||||
#-----| -> ... = ...
|
||||
|
||||
# 97| Hash
|
||||
#-----| -> a
|
||||
|
||||
# 97| Pair
|
||||
#-----| -> c
|
||||
|
||||
@@ -2473,7 +2476,7 @@ cfg.rb:
|
||||
#-----| -> f
|
||||
|
||||
# 97| Pair
|
||||
#-----| -> {...}
|
||||
#-----| -> call to []
|
||||
|
||||
# 97| "f"
|
||||
#-----| -> Pair
|
||||
@@ -2485,11 +2488,14 @@ cfg.rb:
|
||||
#-----| -> parameters
|
||||
|
||||
# 98| map2
|
||||
#-----| -> map1
|
||||
#-----| -> Hash
|
||||
|
||||
# 98| {...}
|
||||
# 98| call to []
|
||||
#-----| -> ... = ...
|
||||
|
||||
# 98| Hash
|
||||
#-----| -> map1
|
||||
|
||||
# 98| ** ...
|
||||
#-----| -> x
|
||||
|
||||
@@ -2512,7 +2518,7 @@ cfg.rb:
|
||||
#-----| -> "y"
|
||||
|
||||
# 98| ** ...
|
||||
#-----| -> {...}
|
||||
#-----| -> call to []
|
||||
|
||||
# 98| map1
|
||||
#-----| -> ** ...
|
||||
|
||||
@@ -148,6 +148,12 @@ positionalArguments
|
||||
| cfg.rb:90:10:90:26 | call to [] | cfg.rb:90:21:90:25 | 3.4e5 |
|
||||
| cfg.rb:91:6:91:10 | ... > ... | cfg.rb:91:10:91:10 | 3 |
|
||||
| cfg.rb:92:3:92:8 | call to puts | cfg.rb:92:8:92:8 | x |
|
||||
| cfg.rb:97:8:97:39 | call to [] | cfg.rb:97:10:97:19 | Pair |
|
||||
| cfg.rb:97:8:97:39 | call to [] | cfg.rb:97:22:97:29 | Pair |
|
||||
| cfg.rb:97:8:97:39 | call to [] | cfg.rb:97:32:97:37 | Pair |
|
||||
| cfg.rb:98:8:98:36 | call to [] | cfg.rb:98:10:98:15 | ** ... |
|
||||
| cfg.rb:98:8:98:36 | call to [] | cfg.rb:98:18:98:27 | Pair |
|
||||
| cfg.rb:98:8:98:36 | call to [] | cfg.rb:98:30:98:35 | ** ... |
|
||||
| cfg.rb:102:3:102:12 | call to puts | cfg.rb:102:8:102:12 | value |
|
||||
| cfg.rb:103:10:103:20 | ...[...] | cfg.rb:103:17:103:19 | key |
|
||||
| cfg.rb:108:1:108:12 | call to puts | cfg.rb:108:6:108:12 | ( ... ) |
|
||||
@@ -344,3 +350,4 @@ positionalArguments
|
||||
keywordArguments
|
||||
| cfg.html.erb:6:9:6:58 | call to stylesheet_link_tag | media | cfg.html.erb:6:54:6:58 | "all" |
|
||||
| cfg.html.erb:12:11:12:33 | call to link_to | id | cfg.html.erb:12:31:12:33 | "a" |
|
||||
| cfg.rb:97:8:97:39 | call to [] | e | cfg.rb:97:35:97:37 | "f" |
|
||||
|
||||
Reference in New Issue
Block a user