|
| 1 | +private import codeql.ruby.AST |
| 2 | +private import AST |
| 3 | +private import Constant |
| 4 | +private import TreeSitter |
| 5 | +private import codeql.ruby.controlflow.CfgNodes |
| 6 | + |
| 7 | +int parseInteger(Ruby::Integer i) { |
| 8 | + exists(string s | s = i.getValue().toLowerCase().replaceAll("_", "") | |
| 9 | + s.charAt(0) != "0" and |
| 10 | + result = s.toInt() |
| 11 | + or |
| 12 | + exists(string str, string values, int shift | |
| 13 | + s.matches("0b%") and |
| 14 | + values = "01" and |
| 15 | + str = s.suffix(2) and |
| 16 | + shift = 1 |
| 17 | + or |
| 18 | + s.matches("0x%") and |
| 19 | + values = "0123456789abcdef" and |
| 20 | + str = s.suffix(2) and |
| 21 | + shift = 4 |
| 22 | + or |
| 23 | + s.charAt(0) = "0" and |
| 24 | + not s.charAt(1) = ["b", "x", "o"] and |
| 25 | + values = "01234567" and |
| 26 | + str = s.suffix(1) and |
| 27 | + shift = 3 |
| 28 | + or |
| 29 | + s.matches("0o%") and |
| 30 | + values = "01234567" and |
| 31 | + str = s.suffix(2) and |
| 32 | + shift = 3 |
| 33 | + | |
| 34 | + result = |
| 35 | + sum(int index, string c, int v, int exp | |
| 36 | + c = str.charAt(index) and |
| 37 | + v = values.indexOf(c.toLowerCase()) and |
| 38 | + exp = str.length() - index - 1 |
| 39 | + | |
| 40 | + v.bitShiftLeft((str.length() - index - 1) * shift) |
| 41 | + ) |
| 42 | + ) |
| 43 | + ) |
| 44 | +} |
| 45 | + |
| 46 | +private class RequiredIntegerLiteralConstantValue extends RequiredConstantValue { |
| 47 | + override predicate requiredInt(int i) { i = any(IntegerLiteral il).getValue() } |
| 48 | +} |
| 49 | + |
| 50 | +abstract class IntegerLiteralImpl extends Expr, TIntegerLiteral { |
| 51 | + abstract int getValueImpl(); |
| 52 | +} |
| 53 | + |
| 54 | +class IntegerLiteralReal extends IntegerLiteralImpl, TIntegerLiteralReal { |
| 55 | + private Ruby::Integer g; |
| 56 | + |
| 57 | + IntegerLiteralReal() { this = TIntegerLiteralReal(g) } |
| 58 | + |
| 59 | + final override int getValueImpl() { result = parseInteger(g) } |
| 60 | + |
| 61 | + final override string toString() { result = g.getValue() } |
| 62 | +} |
| 63 | + |
| 64 | +class IntegerLiteralSynth extends IntegerLiteralImpl, TIntegerLiteralSynth { |
| 65 | + private int value; |
| 66 | + |
| 67 | + IntegerLiteralSynth() { this = TIntegerLiteralSynth(_, _, value) } |
| 68 | + |
| 69 | + final override int getValueImpl() { result = value } |
| 70 | + |
| 71 | + final override string toString() { result = value.toString() } |
| 72 | +} |
| 73 | + |
| 74 | +// TODO: implement properly |
| 75 | +float parseFloat(Ruby::Float f) { result = f.getValue().toFloat() } |
| 76 | + |
| 77 | +private class RequiredFloatConstantValue extends RequiredConstantValue { |
| 78 | + override predicate requiredFloat(float f) { f = parseFloat(_) } |
| 79 | +} |
| 80 | + |
| 81 | +predicate isRationalValue(Ruby::Rational r, int numerator, int denominator) { |
| 82 | + numerator = parseInteger(r.getChild()) and |
| 83 | + denominator = 1 |
| 84 | + or |
| 85 | + exists(Ruby::Float f, string regex, string before, string after | |
| 86 | + f = r.getChild() and |
| 87 | + regex = "([^.]*)\\.(.*)" and |
| 88 | + before = f.getValue().regexpCapture(regex, 1) and |
| 89 | + after = f.getValue().regexpCapture(regex, 2) and |
| 90 | + numerator = before.toInt() * denominator + after.toInt() and |
| 91 | + denominator = 10.pow(after.length()) |
| 92 | + ) |
| 93 | +} |
| 94 | + |
| 95 | +private class RequiredRationalConstantValue extends RequiredConstantValue { |
| 96 | + override predicate requiredRational(int numerator, int denominator) { |
| 97 | + isRationalValue(_, numerator, denominator) |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +float getComplexValue(Ruby::Complex c) { |
| 102 | + exists(string s | |
| 103 | + s = c.getValue() and |
| 104 | + result = s.prefix(s.length() - 1).toFloat() |
| 105 | + ) |
| 106 | +} |
| 107 | + |
| 108 | +private class RequiredComplexConstantValue extends RequiredConstantValue { |
| 109 | + override predicate requiredComplex(float real, float imaginary) { |
| 110 | + real = 0 and |
| 111 | + imaginary = getComplexValue(_) |
| 112 | + } |
| 113 | +} |
| 114 | + |
| 115 | +class TrueLiteral extends BooleanLiteral, TTrueLiteral { |
| 116 | + private Ruby::True g; |
| 117 | + |
| 118 | + TrueLiteral() { this = TTrueLiteral(g) } |
| 119 | + |
| 120 | + final override string toString() { result = g.getValue() } |
| 121 | + |
| 122 | + final override predicate isTrue() { any() } |
| 123 | +} |
| 124 | + |
| 125 | +class FalseLiteral extends BooleanLiteral, TFalseLiteral { |
| 126 | + private Ruby::False g; |
| 127 | + |
| 128 | + FalseLiteral() { this = TFalseLiteral(g) } |
| 129 | + |
| 130 | + final override string toString() { result = g.getValue() } |
| 131 | + |
| 132 | + final override predicate isFalse() { any() } |
| 133 | +} |
| 134 | + |
| 135 | +private class RequiredEncodingLiteralConstantValue extends RequiredConstantValue { |
| 136 | + override predicate requiredString(string s) { s = "UTF-8" } |
| 137 | +} |
| 138 | + |
| 139 | +private class RequiredLineLiteralConstantValue extends RequiredConstantValue { |
| 140 | + override predicate requiredInt(int i) { i = any(LineLiteral ll).getLocation().getStartLine() } |
| 141 | +} |
| 142 | + |
| 143 | +private class RequiredFileLiteralConstantValue extends RequiredConstantValue { |
| 144 | + override predicate requiredString(string s) { |
| 145 | + s = any(FileLiteral fl).getLocation().getFile().getAbsolutePath() |
| 146 | + } |
| 147 | +} |
| 148 | + |
| 149 | +private class RequiredStringTextComponentConstantValue extends RequiredConstantValue { |
| 150 | + override predicate requiredString(string s) { |
| 151 | + s = any(Ruby::Token t | exists(TStringTextComponentNonRegexp(t))).getValue() |
| 152 | + } |
| 153 | +} |
| 154 | + |
| 155 | +private class RequiredStringEscapeSequenceComponentConstantValue extends RequiredConstantValue { |
| 156 | + override predicate requiredString(string s) { |
| 157 | + s = any(Ruby::Token t | exists(TStringEscapeSequenceComponentNonRegexp(t))).getValue() |
| 158 | + } |
| 159 | +} |
| 160 | + |
| 161 | +class TRegExpComponent = |
| 162 | + TStringTextComponentRegexp or TStringEscapeSequenceComponentRegexp or |
| 163 | + TStringInterpolationComponentRegexp; |
| 164 | + |
| 165 | +// Exclude components that are children of a free-spacing regex. |
| 166 | +// We do this because `ParseRegExp.qll` cannot handle free-spacing regexes. |
| 167 | +string getRegExpTextComponentValue(RegExpTextComponent c) { |
| 168 | + exists(Ruby::Token t | |
| 169 | + c = TStringTextComponentRegexp(t) and |
| 170 | + not c.getParent().(RegExpLiteral).hasFreeSpacingFlag() and |
| 171 | + result = t.getValue() |
| 172 | + ) |
| 173 | +} |
| 174 | + |
| 175 | +private class RequiredRegExpTextComponentConstantValue extends RequiredConstantValue { |
| 176 | + override predicate requiredString(string s) { s = getRegExpTextComponentValue(_) } |
| 177 | +} |
| 178 | + |
| 179 | +// Exclude components that are children of a free-spacing regex. |
| 180 | +// We do this because `ParseRegExp.qll` cannot handle free-spacing regexes. |
| 181 | +string getRegExpEscapeSequenceComponentValue(RegExpEscapeSequenceComponent c) { |
| 182 | + exists(Ruby::EscapeSequence e | |
| 183 | + c = TStringEscapeSequenceComponentRegexp(e) and |
| 184 | + not c.getParent().(RegExpLiteral).hasFreeSpacingFlag() and |
| 185 | + result = e.getValue() |
| 186 | + ) |
| 187 | +} |
| 188 | + |
| 189 | +private class RequiredRegExpEscapeSequenceComponentConstantValue extends RequiredConstantValue { |
| 190 | + override predicate requiredString(string s) { s = getRegExpEscapeSequenceComponentValue(_) } |
| 191 | +} |
| 192 | + |
| 193 | +class RegularStringLiteral extends StringLiteral, TRegularStringLiteral { |
| 194 | + private Ruby::String g; |
| 195 | + |
| 196 | + RegularStringLiteral() { this = TRegularStringLiteral(g) } |
| 197 | + |
| 198 | + final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) } |
| 199 | +} |
| 200 | + |
| 201 | +class BareStringLiteral extends StringLiteral, TBareStringLiteral { |
| 202 | + private Ruby::BareString g; |
| 203 | + |
| 204 | + BareStringLiteral() { this = TBareStringLiteral(g) } |
| 205 | + |
| 206 | + final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) } |
| 207 | +} |
| 208 | + |
| 209 | +// Tree-sitter gives us value text including the colon, which we skip. |
| 210 | +string getSimpleSymbolValue(Ruby::SimpleSymbol ss) { result = ss.getValue().suffix(1) } |
| 211 | + |
| 212 | +private class RequiredSimpleSymbolConstantValue extends RequiredConstantValue { |
| 213 | + override predicate requiredSymbol(string s) { s = getSimpleSymbolValue(_) } |
| 214 | +} |
| 215 | + |
| 216 | +private class SimpleSymbolLiteral extends SymbolLiteral, TSimpleSymbolLiteral { |
| 217 | + private Ruby::SimpleSymbol g; |
| 218 | + |
| 219 | + SimpleSymbolLiteral() { this = TSimpleSymbolLiteral(g) } |
| 220 | + |
| 221 | + final override ConstantValue::ConstantSymbolValue getConstantValue() { |
| 222 | + result.isSymbol(getSimpleSymbolValue(g)) |
| 223 | + } |
| 224 | + |
| 225 | + final override string toString() { result = g.getValue() } |
| 226 | +} |
| 227 | + |
| 228 | +class ComplexSymbolLiteral extends SymbolLiteral, TComplexSymbolLiteral { } |
| 229 | + |
| 230 | +class DelimitedSymbolLiteral extends ComplexSymbolLiteral, TDelimitedSymbolLiteral { |
| 231 | + private Ruby::DelimitedSymbol g; |
| 232 | + |
| 233 | + DelimitedSymbolLiteral() { this = TDelimitedSymbolLiteral(g) } |
| 234 | + |
| 235 | + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } |
| 236 | +} |
| 237 | + |
| 238 | +class BareSymbolLiteral extends ComplexSymbolLiteral, TBareSymbolLiteral { |
| 239 | + private Ruby::BareSymbol g; |
| 240 | + |
| 241 | + BareSymbolLiteral() { this = TBareSymbolLiteral(g) } |
| 242 | + |
| 243 | + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } |
| 244 | +} |
| 245 | + |
| 246 | +private class RequiredHashKeySymbolConstantValue extends RequiredConstantValue { |
| 247 | + override predicate requiredSymbol(string s) { s = any(Ruby::HashKeySymbol h).getValue() } |
| 248 | +} |
| 249 | + |
| 250 | +private class HashKeySymbolLiteral extends SymbolLiteral, THashKeySymbolLiteral { |
| 251 | + private Ruby::HashKeySymbol g; |
| 252 | + |
| 253 | + HashKeySymbolLiteral() { this = THashKeySymbolLiteral(g) } |
| 254 | + |
| 255 | + final override ConstantValue::ConstantSymbolValue getConstantValue() { |
| 256 | + result.isSymbol(g.getValue()) |
| 257 | + } |
| 258 | + |
| 259 | + final override string toString() { result = ":" + g.getValue() } |
| 260 | +} |
| 261 | + |
| 262 | +private class RequiredCharacterConstantValue extends RequiredConstantValue { |
| 263 | + override predicate requiredString(string s) { s = any(Ruby::Character c).getValue() } |
| 264 | +} |
| 265 | + |
| 266 | +abstract class ArrayLiteralImpl extends Literal, TArrayLiteral { |
| 267 | + abstract Expr getElementImpl(int n); |
| 268 | + |
| 269 | + abstract int getNumberOfElementsImpl(); |
| 270 | +} |
| 271 | + |
| 272 | +class RegularArrayLiteral extends ArrayLiteralImpl, TRegularArrayLiteral { |
| 273 | + private Ruby::Array g; |
| 274 | + |
| 275 | + RegularArrayLiteral() { this = TRegularArrayLiteral(g) } |
| 276 | + |
| 277 | + final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) } |
| 278 | + |
| 279 | + final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) } |
| 280 | + |
| 281 | + final override string toString() { result = "[...]" } |
| 282 | +} |
| 283 | + |
| 284 | +class StringArrayLiteral extends ArrayLiteralImpl, TStringArrayLiteral { |
| 285 | + private Ruby::StringArray g; |
| 286 | + |
| 287 | + StringArrayLiteral() { this = TStringArrayLiteral(g) } |
| 288 | + |
| 289 | + final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) } |
| 290 | + |
| 291 | + final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) } |
| 292 | + |
| 293 | + final override string toString() { result = "%w(...)" } |
| 294 | +} |
| 295 | + |
| 296 | +class SymbolArrayLiteral extends ArrayLiteralImpl, TSymbolArrayLiteral { |
| 297 | + private Ruby::SymbolArray g; |
| 298 | + |
| 299 | + SymbolArrayLiteral() { this = TSymbolArrayLiteral(g) } |
| 300 | + |
| 301 | + final override Expr getElementImpl(int i) { toGenerated(result) = g.getChild(i) } |
| 302 | + |
| 303 | + final override int getNumberOfElementsImpl() { result = count(g.getChild(_)) } |
| 304 | + |
| 305 | + final override string toString() { result = "%i(...)" } |
| 306 | +} |
| 307 | + |
| 308 | +class HashLiteralImpl extends Literal, THashLiteral { |
| 309 | + Ruby::Hash g; |
| 310 | + |
| 311 | + HashLiteralImpl() { this = THashLiteral(g) } |
| 312 | + |
| 313 | + final int getNumberOfElementsImpl() { result = count(g.getChild(_)) } |
| 314 | +} |
| 315 | + |
| 316 | +class RangeLiteralReal extends RangeLiteral, TRangeLiteralReal { |
| 317 | + private Ruby::Range g; |
| 318 | + |
| 319 | + RangeLiteralReal() { this = TRangeLiteralReal(g) } |
| 320 | + |
| 321 | + final override Expr getBegin() { toGenerated(result) = g.getBegin() } |
| 322 | + |
| 323 | + final override Expr getEnd() { toGenerated(result) = g.getEnd() } |
| 324 | + |
| 325 | + final override predicate isInclusive() { g instanceof @ruby_range_dotdot } |
| 326 | + |
| 327 | + final override predicate isExclusive() { g instanceof @ruby_range_dotdotdot } |
| 328 | +} |
| 329 | + |
| 330 | +class RangeLiteralSynth extends RangeLiteral, TRangeLiteralSynth { |
| 331 | + private boolean inclusive; |
| 332 | + |
| 333 | + RangeLiteralSynth() { this = TRangeLiteralSynth(_, _, inclusive) } |
| 334 | + |
| 335 | + final override Expr getBegin() { result = TIntegerLiteralSynth(this, 0, _) } |
| 336 | + |
| 337 | + final override Expr getEnd() { result = TIntegerLiteralSynth(this, 1, _) } |
| 338 | + |
| 339 | + final override predicate isInclusive() { inclusive = true } |
| 340 | + |
| 341 | + final override predicate isExclusive() { inclusive = false } |
| 342 | +} |
| 343 | + |
| 344 | +private string getMethodName(MethodName::Token t) { |
| 345 | + result = t.(Ruby::Token).getValue() |
| 346 | + or |
| 347 | + result = t.(Ruby::Setter).getName().getValue() + "=" |
| 348 | +} |
| 349 | + |
| 350 | +private class RequiredMethodNameConstantValue extends RequiredConstantValue { |
| 351 | + override predicate requiredString(string s) { s = getMethodName(_) } |
| 352 | +} |
| 353 | + |
| 354 | +class TokenMethodName extends MethodName, TTokenMethodName { |
| 355 | + private MethodName::Token g; |
| 356 | + |
| 357 | + TokenMethodName() { this = TTokenMethodName(g) } |
| 358 | + |
| 359 | + final override ConstantValue::ConstantStringValue getConstantValue() { |
| 360 | + result.isString(getMethodName(g)) |
| 361 | + } |
| 362 | + |
| 363 | + final override string toString() { result = getMethodName(g) } |
| 364 | +} |
0 commit comments