mirror of
https://github.com/github/codeql.git
synced 2025-12-20 02:44:30 +01:00
231 lines
7.4 KiB
Plaintext
231 lines
7.4 KiB
Plaintext
/**
|
|
* Provides classes for working with xUnit.js tests.
|
|
*/
|
|
|
|
import javascript
|
|
|
|
/** Holds if an initialization of xUnit.js is detected. */
|
|
private predicate xUnitDetected() {
|
|
// look for `Function.RegisterNamespace("xUnit.js");`
|
|
exists(MethodCallExpr mc |
|
|
mc.getParent() instanceof ExprStmt and
|
|
mc = DataFlow::globalVarRef("Function").getAMemberCall("RegisterNamespace").asExpr() and
|
|
mc.getNumArgument() = 1 and
|
|
mc.getArgument(0).getStringValue() = "xUnit.js"
|
|
)
|
|
}
|
|
|
|
/** Holds if `e` looks like an xUnit.js attribute, possibly with arguments. */
|
|
private predicate possiblyAttribute(Expr e, string name) {
|
|
exists(Identifier id | id = e or id = e.(CallExpr).getCallee() |
|
|
name = id.getName() and
|
|
name = ["Async", "Data", "Fact", "Fixture", "Import", "ImportJson", "Skip", "Trait"]
|
|
)
|
|
}
|
|
|
|
/**
|
|
* A bracketed list of expressions.
|
|
*
|
|
* Depending on their syntactic position, such lists will either be parsed as
|
|
* array expressions, or as a property index expression where the indexing
|
|
* expression is a comma expression: for example, in `[a, b][c, d]`, the
|
|
* list `[a, b]` is an array expression, whereas `[c, d]` is an indexing
|
|
* expression.
|
|
*
|
|
* We also allow singleton lists, as in `[a][b]`.
|
|
*/
|
|
abstract private class BracketedListOfExpressions extends Expr {
|
|
/** Gets the `i`th element expression of this list. */
|
|
abstract Expr getElement(int i);
|
|
}
|
|
|
|
/**
|
|
* An array expression viewed as a bracketed list of expressions.
|
|
*/
|
|
private class ArrayExprIsABracketedListOfExpressions extends ArrayExpr, BracketedListOfExpressions {
|
|
/** Gets the `i`th element of this array literal. */
|
|
override Expr getElement(int i) { result = ArrayExpr.super.getElement(i) }
|
|
}
|
|
|
|
/**
|
|
* A bracketed list of expressions that appears right after another such list,
|
|
* and is hence parsed as an index expression.
|
|
*
|
|
* Note that the index expression itself does not include the opening and
|
|
* closing brackets, for which we compensate by overriding `getFirstToken()`
|
|
* and `getLastToken()`.
|
|
*/
|
|
private class IndexExprIndexIsBracketedListOfExpressions extends BracketedListOfExpressions {
|
|
IndexExprIndexIsBracketedListOfExpressions() {
|
|
exists(IndexExpr idx, Expr base |
|
|
base = idx.getBase() and
|
|
this = idx.getIndex() and
|
|
// restrict to case where previous expression is also bracketed
|
|
(base instanceof IndexExpr or base instanceof ArrayExpr)
|
|
)
|
|
}
|
|
|
|
override Expr getElement(int i) {
|
|
result = this.(SeqExpr).getChildExpr(i)
|
|
or
|
|
not this instanceof SeqExpr and i = 0 and result = this
|
|
}
|
|
|
|
override Token getFirstToken() {
|
|
// include opening bracket
|
|
result = BracketedListOfExpressions.super.getFirstToken().getPreviousToken()
|
|
}
|
|
|
|
override Token getLastToken() {
|
|
// include closing bracket
|
|
result = BracketedListOfExpressions.super.getLastToken().getNextToken()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holds if `ann` annotates `trg`, possibly skipping over other intervening
|
|
* annotations.
|
|
*
|
|
* We use a token-level definition, since depending on the number of annotations involved
|
|
* the AST structure can become pretty complicated.
|
|
*/
|
|
private predicate annotationTarget(BracketedListOfExpressions ann, XUnitTarget trg) {
|
|
// every element looks like an attribute
|
|
forex(Expr e | e = ann.getElement(_) | possiblyAttribute(e, _)) and
|
|
// followed directly either by a target or by another annotation
|
|
exists(Token next | next = ann.getLastToken().getNextToken() |
|
|
trg.getFirstToken() = next
|
|
or
|
|
exists(BracketedListOfExpressions ann2 | ann2.getFirstToken() = next |
|
|
annotationTarget(ann2, trg)
|
|
)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* An xUnit.js annotation, such as `[Fixture]` or `[Data(23)]` annotating
|
|
* a target declaration or definition.
|
|
*/
|
|
class XUnitAnnotation extends Expr {
|
|
XUnitAnnotation() {
|
|
xUnitDetected() and
|
|
annotationTarget(this, _)
|
|
}
|
|
|
|
/** Gets the declaration or definition to which this annotation belongs. */
|
|
XUnitTarget getTarget() { annotationTarget(this, result) }
|
|
|
|
/** Gets the `i`th attribute of this annotation. */
|
|
Expr getAttribute(int i) { result = this.(BracketedListOfExpressions).getElement(i) }
|
|
|
|
/** Gets an attribute of this annotation. */
|
|
Expr getAnAttribute() { result = this.getAttribute(_) }
|
|
|
|
/** Gets the number of attributes of this annotation. */
|
|
int getNumAttribute() { result = strictcount(this.getAnAttribute()) }
|
|
|
|
/**
|
|
* Holds if this element is at the specified location.
|
|
* The location spans column `startcolumn` of line `startline` to
|
|
* column `endcolumn` of line `endline` in file `filepath`.
|
|
* For more information, see
|
|
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
|
*/
|
|
predicate hasLocationInfo(
|
|
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
|
) {
|
|
// extend location to cover brackets
|
|
exists(Location l1, Location l2 |
|
|
l1 = this.getFirstToken().getLocation() and
|
|
l2 = this.getLastToken().getLocation()
|
|
|
|
|
filepath = l1.getFile().getAbsolutePath() and
|
|
startline = l1.getStartLine() and
|
|
startcolumn = l1.getStartColumn() and
|
|
endline = l2.getEndLine() and
|
|
endcolumn = l2.getEndColumn()
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A declaration or definition that can serve as the target of an xUnit.js
|
|
* annotation: a function declaration, a variable declaration, or an assignment.
|
|
*/
|
|
class XUnitTarget extends Stmt {
|
|
XUnitTarget() {
|
|
this instanceof FunctionDeclStmt or
|
|
this instanceof VarDeclStmt or
|
|
this.(ExprStmt).getExpr() instanceof AssignExpr
|
|
}
|
|
|
|
/** Gets an annotation of which this is the target, if any. */
|
|
XUnitAnnotation getAnAnnotation() { this = result.getTarget() }
|
|
}
|
|
|
|
/**
|
|
* An xUnit.js attribute appearing in an annotation.
|
|
*/
|
|
class XUnitAttribute extends Expr {
|
|
XUnitAttribute() { exists(XUnitAnnotation ann | this = ann.getAnAttribute()) }
|
|
|
|
/** Gets the name of this attribute. */
|
|
string getName() { possiblyAttribute(this, result) }
|
|
|
|
/** Gets the `i`th parameter of this attribute. */
|
|
Expr getParameter(int i) { result = this.(CallExpr).getArgument(i) }
|
|
|
|
/** Gets a parameter of this attribute. */
|
|
Expr getAParameter() { result = this.getParameter(_) }
|
|
|
|
/** Gets the number of parameters of this attribute. */
|
|
int getNumParameter() { result = count(this.getAParameter()) }
|
|
}
|
|
|
|
/**
|
|
* A function with an xUnit.js annotation.
|
|
*/
|
|
private class XUnitAnnotatedFunction extends Function {
|
|
XUnitAnnotatedFunction() {
|
|
exists(XUnitTarget target | exists(target.getAnAnnotation()) |
|
|
this = target or
|
|
this = target.(VarDeclStmt).getADecl().getInit() or
|
|
this = target.(ExprStmt).getExpr().(AssignExpr).getRhs()
|
|
)
|
|
}
|
|
|
|
/** Gets an xUnit.js annotation on this function. */
|
|
XUnitAnnotation getAnAnnotation() {
|
|
result = this.(XUnitTarget).getAnAnnotation() or
|
|
result = this.(Expr).getEnclosingStmt().(XUnitTarget).getAnAnnotation()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An xUnit.js `Fixture` annotation.
|
|
*/
|
|
class XUnitFixtureAnnotation extends XUnitAnnotation {
|
|
XUnitFixtureAnnotation() { this.getAnAttribute().accessesGlobal("Fixture") }
|
|
}
|
|
|
|
/**
|
|
* An xUnit.js fixture.
|
|
*/
|
|
class XUnitFixture extends XUnitAnnotatedFunction {
|
|
XUnitFixture() { this.getAnAnnotation() instanceof XUnitFixtureAnnotation }
|
|
}
|
|
|
|
/**
|
|
* An xUnit.js `Fact` annotation.
|
|
*/
|
|
class XUnitFactAnnotation extends XUnitAnnotation {
|
|
XUnitFactAnnotation() { this.getAnAttribute().accessesGlobal("Fact") }
|
|
}
|
|
|
|
/**
|
|
* An xUnit.js fact.
|
|
*/
|
|
class XUnitFact extends XUnitAnnotatedFunction {
|
|
XUnitFact() { this.getAnAnnotation() instanceof XUnitFactAnnotation }
|
|
}
|