diff --git a/ql/consistency-queries/AstConsistency.ql b/ql/consistency-queries/AstConsistency.ql new file mode 100644 index 00000000000..773b6c8b898 --- /dev/null +++ b/ql/consistency-queries/AstConsistency.ql @@ -0,0 +1,10 @@ +import ql +private import codeql_ql.ast.internal.AstNodes as AstNodes + +query AstNode nonTotalGetParent() { + exists(AstNodes::toQL(result).getParent()) and + not exists(result.getParent()) and + not result.getLocation().getStartColumn() = 1 and // startcolumn = 1 <=> top level in file <=> fine to have no parent + not result instanceof YAML::YAMLNode and // parents for YAML doens't work + not (result instanceof QLDoc and result.getLocation().getFile().getExtension() = "dbscheme") // qldoc in dbschemes are not hooked up +} diff --git a/ql/src/codeql_ql/ast/Ast.qll b/ql/src/codeql_ql/ast/Ast.qll index ed776d07a1c..94ca5c2a72b 100644 --- a/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/src/codeql_ql/ast/Ast.qll @@ -56,7 +56,11 @@ class AstNode extends TAstNode { * named `pred`. */ cached - AstNode getAChild(string pred) { none() } + AstNode getAChild(string pred) { + pred = directMember("getAnAnnotation") and result = getAnAnnotation() + or + pred = directMember("getQLDoc") and result = getQLDoc() + } /** * Gets the primary QL class for the ast node. @@ -70,10 +74,7 @@ class AstNode extends TAstNode { predicate hasAnnotation(string name) { this.getAnAnnotation().getName() = name } /** Gets an annotation of this AST node. */ - cached - Annotation getAnAnnotation() { - toQL(this).getParent() = pragma[only_bind_out](toQL(result)).getParent() - } + Annotation getAnAnnotation() { toQL(this).getParent() = toQL(result).getParent() } /** * Gets the predicate that contains this AST node. @@ -119,8 +120,6 @@ class TopLevel extends TTopLevel, AstNode { NewType getANewType() { result = this.getAMember() } override ModuleMember getAChild(string pred) { - pred = directMember("getQLDoc") and result = this.getQLDoc() - or pred = directMember("getAnImport") and result = this.getAnImport() or pred = directMember("getAClass") and result = this.getAClass() @@ -214,7 +213,7 @@ class BuiltinPredicate extends PredicateOrBuiltin, TBuiltin { override string getAPrimaryQlClass() { result = "BuiltinPredicate" } } -private class BuiltinClassless extends BuiltinPredicate, TBuiltinClassless { +class BuiltinClassless extends BuiltinPredicate, TBuiltinClassless { string name; string ret; string args; @@ -228,7 +227,7 @@ private class BuiltinClassless extends BuiltinPredicate, TBuiltinClassless { override PrimitiveType getParameterType(int i) { result.getName() = getArgType(args, i) } } -private class BuiltinMember extends BuiltinPredicate, TBuiltinMember { +class BuiltinMember extends BuiltinPredicate, TBuiltinMember { string name; string qual; string ret; @@ -626,7 +625,7 @@ class TypeExpr extends TType, AstNode { */ Type getResolvedType() { resolveTypeExpr(this, result) } - override ModuleExpr getAChild(string pred) { + override AstNode getAChild(string pred) { result = super.getAChild(pred) or pred = directMember("getModule") and result = this.getModule() @@ -691,11 +690,7 @@ class Declaration extends TDeclaration, AstNode { result = any(Class c).getQLDocFor(this) } - override AstNode getAChild(string pred) { - result = super.getAChild(pred) - or - pred = directMember("getQLDoc") and result = this.getQLDoc() - } + override AstNode getAChild(string pred) { result = super.getAChild(pred) } } /** An entity that can be declared in a module. */ @@ -784,6 +779,8 @@ class Class extends TClass, TypeDeclaration, ModuleDeclaration { or pred = directMember("getASuperType") and result = this.getASuperType() or + pred = directMember("getAnInstanceofType") and result = this.getAnInstanceofType() + or exists(string name | pred = stringIndexedMember("getClassPredicate", name) and result = this.getClassPredicate(name) @@ -860,8 +857,6 @@ class NewTypeBranch extends TNewTypeBranch, PredicateOrBuiltin, TypeDeclaration pred = directMember("getBody") and result = this.getBody() or exists(int i | pred = indexedMember("getField", i) and result = this.getField(i)) - or - pred = directMember("getQLDoc") and result = this.getQLDoc() } } @@ -2196,6 +2191,12 @@ class Annotation extends TAnnotation, AstNode { /** Gets the node corresponding to the field `name`. */ string getName() { result = annot.getName().getValue() } + + override AstNode getAChild(string pred) { + result = super.getAChild(pred) + or + exists(int i | pred = indexedMember("getArgs", i) and result = this.getArgs(i)) + } } /** A `pragma[noinline]` annotation. */ @@ -2414,8 +2415,8 @@ module YAML { /** Gets the database scheme of this qlpack */ File getDBScheme() { - result.getBaseName() = this.getProperty("dbscheme") and - result = file.getParentContainer().getFile(any(string s | s.matches("%.dbscheme"))) + result.getAbsolutePath() = + file.getParentContainer().getAbsolutePath() + "/" + this.getProperty("dbscheme") } pragma[noinline] diff --git a/ql/src/codeql_ql/ast/internal/Builtins.qll b/ql/src/codeql_ql/ast/internal/Builtins.qll index 78b40e56f30..36b00b5eefa 100644 --- a/ql/src/codeql_ql/ast/internal/Builtins.qll +++ b/ql/src/codeql_ql/ast/internal/Builtins.qll @@ -44,7 +44,9 @@ predicate isBuiltinMember(string sig) { "string string.toLowerCase()", "string string.toUpperCase()", "string string.trim()", "int date.daysTo(date)", "int date.getDay()", "int date.getHours()", "int date.getMinutes()", "int date.getMonth()", "int date.getSeconds()", "int date.getYear()", - "string date.toString()", "string date.toISO()", "string int.toUnicode()" + "string date.toString()", "string date.toISO()", "string int.toUnicode()", + "string any.getAQlClass()" + /* getAQlClass is special , see Predicate.qll*/ ] } diff --git a/ql/src/codeql_ql/ast/internal/Module.qll b/ql/src/codeql_ql/ast/internal/Module.qll index faf18cf7954..4c6d3d196c5 100644 --- a/ql/src/codeql_ql/ast/internal/Module.qll +++ b/ql/src/codeql_ql/ast/internal/Module.qll @@ -154,18 +154,13 @@ private predicate resolveSelectionName(Import imp, ContainerOrModule m, int i) { cached private module Cached { - private Module getEnclosingModule0(AstNode n) { - not n instanceof Module and - ( - n = result.getAChild(_) - or - exists(AstNode prev | - result = getEnclosingModule0(prev) and - n = prev.getAChild(_) - ) - ) + private AstNode parent(AstNode n) { + result = n.getParent() and + not n instanceof Module } + private Module getEnclosingModule0(AstNode n) { result = parent*(n.getParent()) } + cached ContainerOrModule getEnclosingModule(AstNode n) { result = TModule(getEnclosingModule0(n)) diff --git a/ql/src/codeql_ql/ast/internal/Predicate.qll b/ql/src/codeql_ql/ast/internal/Predicate.qll index 393ecdf4266..44715c1b0ff 100644 --- a/ql/src/codeql_ql/ast/internal/Predicate.qll +++ b/ql/src/codeql_ql/ast/internal/Predicate.qll @@ -81,7 +81,7 @@ private module Cached { // super calls exists(Super sup, ClassType type, Type supertype | mc.getBase() = sup and - sup.getEnclosingPredicate().(ClassPredicate).getParent().getType() = type and + sup.getEnclosingPredicate().getParent().(Class).getType() = type and supertype in [type.getASuperType(), type.getAnInstanceofType()] and p = supertype.getClassPredicate(mc.getMemberName(), mc.getNumberOfArguments()) ) @@ -110,14 +110,26 @@ private module Cached { ) } + private predicate resolveBuildinPredicateCall(PredicateCall call, BuiltinClassless pred) { + call.getNumberOfArguments() = pred.getArity() and + call.getPredicateName() = pred.getName() + } + cached predicate resolveCall(Call c, PredicateOrBuiltin p) { resolvePredicateCall(c, p) or + not resolvePredicateCall(c, _) and + resolveBuildinPredicateCall(c, p) + or resolveMemberCall(c, p) or not resolvePredicateCall(c, _) and resolveDBRelation(c, p) + or + // getAQlClass() is special + c.(MemberCall).getMemberName() = "getAQlClass" and + p.(BuiltinMember).getName() = "getAQlClass" } } diff --git a/ql/test/callgraph/Bar.qll b/ql/test/callgraph/Bar.qll new file mode 100644 index 00000000000..07add85d637 --- /dev/null +++ b/ql/test/callgraph/Bar.qll @@ -0,0 +1,9 @@ +import ql + +module Firebase { + module Database { + query predicate ref(int i) { i = snapshot() } + } + + int snapshot() { result = 2 } +} diff --git a/ql/test/callgraph/Baz.qll b/ql/test/callgraph/Baz.qll new file mode 100644 index 00000000000..371439d8530 --- /dev/null +++ b/ql/test/callgraph/Baz.qll @@ -0,0 +1,11 @@ +class Foo extends string { + Foo() { this = "Foo" } + + string getImportedPath() { none() } +} + +class Bar extends string, Foo { + Bar() { exists(Foo.super.getImportedPath()) } + + override string getImportedPath() { none() } +} diff --git a/ql/test/callgraph/callgraph.expected b/ql/test/callgraph/callgraph.expected index e4540d519ff..a931da5b1ff 100644 --- a/ql/test/callgraph/callgraph.expected +++ b/ql/test/callgraph/callgraph.expected @@ -1,4 +1,6 @@ getTarget +| Bar.qll:5:38:5:47 | PredicateCall | Bar.qll:8:3:8:31 | ClasslessPredicate snapshot | +| Baz.qll:8:18:8:44 | MemberCall | Baz.qll:4:3:4:37 | ClassPredicate getImportedPath | | Foo.qll:5:26:5:30 | PredicateCall | Foo.qll:3:1:3:26 | ClasslessPredicate foo | | Foo.qll:10:21:10:25 | PredicateCall | Foo.qll:8:3:8:28 | ClassPredicate bar | | Foo.qll:14:30:14:40 | MemberCall | Foo.qll:10:3:10:27 | ClassPredicate baz | diff --git a/ql/test/printAst/printAst.expected b/ql/test/printAst/printAst.expected index eb4340d2adb..6e8f4d30fdd 100644 --- a/ql/test/printAst/printAst.expected +++ b/ql/test/printAst/printAst.expected @@ -196,6 +196,7 @@ nodes | file://:0:0:0:0 | exp | semmle.label | [BuiltinPredicate] exp | | file://:0:0:0:0 | floor | semmle.label | [BuiltinPredicate] floor | | file://:0:0:0:0 | gcd | semmle.label | [BuiltinPredicate] gcd | +| file://:0:0:0:0 | getAQlClass | semmle.label | [BuiltinPredicate] getAQlClass | | file://:0:0:0:0 | getDay | semmle.label | [BuiltinPredicate] getDay | | file://:0:0:0:0 | getHours | semmle.label | [BuiltinPredicate] getHours | | file://:0:0:0:0 | getMinutes | semmle.label | [BuiltinPredicate] getMinutes | @@ -288,6 +289,10 @@ edges | Foo.qll:6:23:6:36 | ComparisonFormula | Foo.qll:6:30:6:30 | ComparisonOp | semmle.order | 14 | | Foo.qll:6:23:6:36 | ComparisonFormula | Foo.qll:6:32:6:36 | String | semmle.label | getRightOperand() | | Foo.qll:6:23:6:36 | ComparisonFormula | Foo.qll:6:32:6:36 | String | semmle.order | 15 | +| Foo.qll:9:1:9:5 | annotation | Foo.qll:9:1:9:5 | annotation | semmle.label | getAnAnnotation() | +| Foo.qll:9:1:9:5 | annotation | Foo.qll:9:1:9:5 | annotation | semmle.order | 16 | +| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:1:9:5 | annotation | semmle.label | getAnAnnotation() | +| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:1:9:5 | annotation | semmle.order | 16 | | Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:21:9:25 | f | semmle.label | getParameter(_) | | Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:21:9:25 | f | semmle.order | 18 | | Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:10:3:10:85 | ComparisonFormula | semmle.label | getBody() |