Files
codeql/python/ql/lib/semmle/python/objects/Sequences.qll
2022-05-11 17:48:27 +01:00

256 lines
7.9 KiB
Plaintext

import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo
private import semmle.python.pointsto.PointsToContext
private import semmle.python.pointsto.MRO
private import semmle.python.types.Builtins
abstract class SequenceObjectInternal extends ObjectInternal {
/** Gets the `n`th item of this sequence, if one exists. */
abstract ObjectInternal getItem(int n);
override boolean booleanValue() {
this.length() = 0 and result = false
or
this.length() != 0 and result = true
}
override boolean isDescriptor() { result = false }
pragma[noinline]
override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) {
none()
}
pragma[noinline]
override predicate descriptorGetInstance(
ObjectInternal instance, ObjectInternal value, CfgOrigin origin
) {
none()
}
pragma[noinline]
override predicate binds(ObjectInternal instance, string name, ObjectInternal descriptor) {
none()
}
override string getName() { none() }
override predicate contextSensitiveCallee() { none() }
override ObjectInternal getIterNext() { result = this.getItem(_) }
}
abstract class TupleObjectInternal extends SequenceObjectInternal {
override string toString() { result = "(" + this.contents(0) + ")" }
private string contents(int n) {
n < 4 and n = this.length() and result = ""
or
n = 3 and this.length() > 3 and result = (this.length() - 3).toString() + " more..."
or
result = this.item(n) + ", " + this.contents(n + 1)
}
private string item(int n) {
result = bounded_toString(this.getItem(n))
or
n in [0 .. this.length() - 1] and
not exists(this.getItem(n)) and
result = "?"
}
/** Gets the class declaration for this object, if it is a declared class. */
override ClassDecl getClassDeclaration() { none() }
/** True if this "object" is a class. */
override boolean isClass() { result = false }
override ObjectInternal getClass() { result = ObjectInternal::builtin("tuple") }
/**
* True if this "object" can be meaningfully analyzed for
* truth or false in comparisons. For example, `None` or `int` can be, but `int()`
* or an unknown string cannot.
*/
override predicate notTestableForEquality() { none() }
/**
* Holds if `obj` is the result of calling `this` and `origin` is
* the origin of `obj`.
*/
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
/**
* Holds if `obj` is the result of calling `this` and `origin` is
* the origin of `obj` with callee context `callee`.
*/
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
override int intValue() { none() }
override string strValue() { none() }
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
pragma[noinline]
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
pragma[noinline]
override predicate attributesUnknown() { none() }
override predicate subscriptUnknown() { none() }
}
/** A tuple built-in to the interpreter, including the empty tuple. */
class BuiltinTupleObjectInternal extends TBuiltinTuple, TupleObjectInternal {
override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() }
override Builtin getBuiltin() { this = TBuiltinTuple(result) }
override ControlFlowNode getOrigin() { none() }
override ObjectInternal getItem(int n) { result.getBuiltin() = this.getBuiltin().getItem(n) }
override int length() {
exists(Builtin b |
b = this.getBuiltin() and
result = count(int n | exists(b.getItem(n)))
)
}
override predicate useOriginAsLegacyObject() { none() }
override predicate isNotSubscriptedType() { any() }
}
/** A tuple declared by a tuple expression in the Python source code */
class PythonTupleObjectInternal extends TPythonTuple, TupleObjectInternal {
override predicate introducedAt(ControlFlowNode node, PointsToContext context) {
this = TPythonTuple(node, context)
}
override Builtin getBuiltin() { none() }
override ControlFlowNode getOrigin() { this = TPythonTuple(result, _) }
override ObjectInternal getItem(int n) {
exists(TupleNode t, PointsToContext context |
this = TPythonTuple(t, context) and
PointsToInternal::pointsTo(t.getElement(n), context, result, _)
)
}
override int length() {
exists(TupleNode t |
this = TPythonTuple(t, _) and
result = count(int n | exists(t.getElement(n)))
)
}
override predicate useOriginAsLegacyObject() { none() }
override predicate isNotSubscriptedType() { any() }
}
/** A tuple created by a `*` parameter */
class VarargsTupleObjectInternal extends TVarargsTuple, TupleObjectInternal {
override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() }
override Builtin getBuiltin() { none() }
override ControlFlowNode getOrigin() { none() }
override ObjectInternal getItem(int n) {
exists(CallNode call, PointsToContext context, int offset, int length |
this = TVarargsTuple(call, context, offset, length) and
n < length and
InterProceduralPointsTo::positional_argument_points_to(call, offset + n, context, result, _)
)
}
override int length() { this = TVarargsTuple(_, _, _, result) }
override predicate useOriginAsLegacyObject() { any() }
override predicate isNotSubscriptedType() { any() }
}
/**
* The `sys.version_info` object. We treat this specially to prevent premature pruning and
* false positives when we are unsure of the actual version of Python that the code is expecting.
*/
class SysVersionInfoObjectInternal extends TSysVersionInfo, SequenceObjectInternal {
override string toString() { result = "sys.version_info" }
override ObjectInternal getItem(int n) {
n = 0 and result = TInt(major_version())
or
n = 1 and result = TInt(minor_version())
}
override predicate introducedAt(ControlFlowNode node, PointsToContext context) { none() }
/** Gets the class declaration for this object, if it is a declared class. */
override ClassDecl getClassDeclaration() {
result = Builtin::special("sys").getMember("version_info").getClass()
}
/** True if this "object" is a class. */
override boolean isClass() { result = false }
override ObjectInternal getClass() { result.getBuiltin() = this.getClassDeclaration() }
override predicate notTestableForEquality() { none() }
/**
* Gets the `Builtin` for this object, if any.
* Objects (except unknown and undefined values) should attempt to return
* exactly one result for either this method or `getOrigin()`.
*/
override Builtin getBuiltin() { none() }
/**
* Gets a control flow node that represents the source origin of this
* objects.
*/
override ControlFlowNode getOrigin() { none() }
/**
* Holds if `obj` is the result of calling `this` and `origin` is
* the origin of `obj`.
*/
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
/**
* Holds if `obj` is the result of calling `this` and `origin` is
* the origin of `obj` with callee context `callee`.
*/
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
none()
}
override int intValue() { none() }
override string strValue() { none() }
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
override predicate attributesUnknown() { none() }
override predicate subscriptUnknown() { none() }
override int length() { result = 5 }
override predicate functionAndOffset(CallableObjectInternal function, int offset) { none() }
override predicate useOriginAsLegacyObject() { any() }
override predicate isNotSubscriptedType() { any() }
}