Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test new instanceof syntax #5891

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions javascript/ql/src/semmle/javascript/Base64.qll
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@ import javascript

module Base64 {
/** A call to a base64 encoder. */
class Encode extends DataFlow::Node {
Encode::Range encode;

Encode() { this = encode }
class Encode extends DataFlow::Node instanceof Encode::Range {

/** Gets the input passed to the encoder. */
DataFlow::Node getInput() { result = encode.getInput() }
DataFlow::Node getInput() { result = this.(Encode::Range).getInput() }

/** Gets the base64-encoded output of the encoder. */
DataFlow::Node getOutput() { result = encode.getOutput() }
DataFlow::Node getOutput() { result = this.(Encode::Range).getOutput() }
}

module Encode {
Expand All @@ -34,16 +31,13 @@ module Base64 {
}

/** A call to a base64 decoder. */
class Decode extends DataFlow::Node {
Decode::Range encode;

Decode() { this = encode }
class Decode extends DataFlow::Node instanceof Decode::Range {

/** Gets the base64-encoded input passed to the decoder. */
DataFlow::Node getInput() { result = encode.getInput() }
DataFlow::Node getInput() { result = this.(Decode::Range).getInput() }

/** Gets the output of the decoder. */
DataFlow::Node getOutput() { result = encode.getOutput() }
DataFlow::Node getOutput() { result = this.(Decode::Range).getOutput() }
}

module Decode {
Expand Down
21 changes: 8 additions & 13 deletions javascript/ql/src/semmle/javascript/Closure.qll
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ module Closure {
/**
* A reference to a Closure namespace.
*/
class ClosureNamespaceRef extends DataFlow::Node {
ClosureNamespaceRef::Range range;

ClosureNamespaceRef() { this = range }
class ClosureNamespaceRef extends DataFlow::Node instanceof ClosureNamespaceRef::Range {

/**
* Gets the namespace being referenced.
*/
string getClosureNamespace() { result = range.getClosureNamespace() }
string getClosureNamespace() {
result = this.(ClosureNamespaceRef::Range).getClosureNamespace()
}
}

module ClosureNamespaceRef {
Expand All @@ -36,8 +35,7 @@ module Closure {
/**
* A data flow node that returns the value of a closure namespace.
*/
class ClosureNamespaceAccess extends ClosureNamespaceRef {
override ClosureNamespaceAccess::Range range;
class ClosureNamespaceAccess extends ClosureNamespaceRef instanceof ClosureNamespaceAccess::Range {
}

module ClosureNamespaceAccess {
Expand Down Expand Up @@ -80,8 +78,7 @@ module Closure {
/**
* A top-level call to `goog.provide`.
*/
class ClosureProvideCall extends ClosureNamespaceRef, DataFlow::MethodCallNode {
override DefaultClosureProvideCall range;
class ClosureProvideCall extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureProvideCall {
}

/**
Expand All @@ -94,8 +91,7 @@ module Closure {
/**
* A call to `goog.require`.
*/
class ClosureRequireCall extends ClosureNamespaceAccess, DataFlow::MethodCallNode {
override DefaultClosureRequireCall range;
class ClosureRequireCall extends ClosureNamespaceAccess, DataFlow::MethodCallNode instanceof DefaultClosureRequireCall {
}

/**
Expand All @@ -111,8 +107,7 @@ module Closure {
/**
* A top-level call to `goog.module` or `goog.declareModuleId`.
*/
class ClosureModuleDeclaration extends ClosureNamespaceRef, DataFlow::MethodCallNode {
override DefaultClosureModuleDeclaration range;
class ClosureModuleDeclaration extends ClosureNamespaceRef, DataFlow::MethodCallNode instanceof DefaultClosureModuleDeclaration {
}

private GlobalVariable googVariable() { variables(result, "goog", any(GlobalScope sc)) }
Expand Down
11 changes: 4 additions & 7 deletions javascript/ql/src/semmle/javascript/InclusionTests.qll
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,21 @@ private import javascript
* ~A.indexOf(B)
* ```
*/
class InclusionTest extends DataFlow::Node {
InclusionTest::Range range;

InclusionTest() { this = range }
class InclusionTest extends DataFlow::Node instanceof InclusionTest::Range {

/** Gets the `A` in `A.includes(B)`. */
DataFlow::Node getContainerNode() { result = range.getContainerNode() }
DataFlow::Node getContainerNode() { result = this.(InclusionTest::Range).getContainerNode() }

/** Gets the `B` in `A.includes(B)`. */
DataFlow::Node getContainedNode() { result = range.getContainedNode() }
DataFlow::Node getContainedNode() { result = this.(InclusionTest::Range).getContainedNode() }

/**
* Gets the polarity of the check.
*
* If the polarity is `false` the check returns `true` if the container does not contain
* the given element.
*/
boolean getPolarity() { result = range.getPolarity() }
boolean getPolarity() { result = this.(InclusionTest::Range).getPolarity() }
}

module InclusionTest {
Expand Down
13 changes: 5 additions & 8 deletions javascript/ql/src/semmle/javascript/MembershipCandidates.qll
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,32 @@ import javascript
*
* Additional candidates can be added by subclassing `MembershipCandidate::Range`
*/
class MembershipCandidate extends DataFlow::Node {
MembershipCandidate::Range range;

MembershipCandidate() { this = range }
class MembershipCandidate extends DataFlow::Node instanceof MembershipCandidate::Range {

/**
* Gets the expression that performs the membership test, if any.
*/
DataFlow::Node getTest() { result = range.getTest() }
DataFlow::Node getTest() { result = this.(MembershipCandidate::Range).getTest() }

/**
* Gets a string that this candidate is tested against, if
* it can be determined.
*/
string getAMemberString() { result = range.getAMemberString() }
string getAMemberString() { result = this.(MembershipCandidate::Range).getAMemberString() }

/**
* Gets a node that this candidate is tested against, if
* it can be determined.
*/
DataFlow::Node getAMemberNode() { result = range.getAMemberNode() }
DataFlow::Node getAMemberNode() { result = this.(MembershipCandidate::Range).getAMemberNode() }

/**
* Gets the polarity of the test.
*
* If the polarity is `false` the test returns `true` if the
* collection does not contain this candidate.
*/
boolean getTestPolarity() { result = range.getTestPolarity() }
boolean getTestPolarity() { result = this.(MembershipCandidate::Range).getTestPolarity() }
}

/**
Expand Down
35 changes: 13 additions & 22 deletions javascript/ql/src/semmle/javascript/StringOps.qll
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,25 @@ module StringOps {
/**
* A expression that is equivalent to `A.startsWith(B)` or `!A.startsWith(B)`.
*/
class StartsWith extends DataFlow::Node {
StartsWith::Range range;

StartsWith() { range = this }
class StartsWith extends DataFlow::Node instanceof StartsWith::Range {

/**
* Gets the `A` in `A.startsWith(B)`.
*/
DataFlow::Node getBaseString() { result = range.getBaseString() }
DataFlow::Node getBaseString() { result = this.(StartsWith::Range).getBaseString() }

/**
* Gets the `B` in `A.startsWith(B)`.
*/
DataFlow::Node getSubstring() { result = range.getSubstring() }
DataFlow::Node getSubstring() { result = this.(StartsWith::Range).getSubstring() }

/**
* Gets the polarity of the check.
*
* If the polarity is `false` the check returns `true` if the string does not start
* with the given substring.
*/
boolean getPolarity() { result = range.getPolarity() }
boolean getPolarity() { result = this.(StartsWith::Range).getPolarity() }
}

module StartsWith {
Expand Down Expand Up @@ -237,28 +234,25 @@ module StringOps {
/**
* An expression that is equivalent to `A.endsWith(B)` or `!A.endsWith(B)`.
*/
class EndsWith extends DataFlow::Node {
EndsWith::Range range;

EndsWith() { this = range }
class EndsWith extends DataFlow::Node instanceof EndsWith::Range {

/**
* Gets the `A` in `A.startsWith(B)`.
*/
DataFlow::Node getBaseString() { result = range.getBaseString() }
DataFlow::Node getBaseString() { result = this.(EndsWith::Range).getBaseString() }

/**
* Gets the `B` in `A.startsWith(B)`.
*/
DataFlow::Node getSubstring() { result = range.getSubstring() }
DataFlow::Node getSubstring() { result = this.(EndsWith::Range).getSubstring() }

/**
* Gets the polarity if the check.
*
* If the polarity is `false` the check returns `true` if the string does not end
* with the given substring.
*/
boolean getPolarity() { result = range.getPolarity() }
boolean getPolarity() { result = this.(EndsWith::Range).getPolarity() }
}

module EndsWith {
Expand Down Expand Up @@ -662,39 +656,36 @@ module StringOps {
* if (!match) { ... } // <--- 'match' is the RegExpTest
* ```
*/
class RegExpTest extends DataFlow::Node {
RegExpTest::Range range;

RegExpTest() { this = range }
class RegExpTest extends DataFlow::Node instanceof RegExpTest::Range {

/**
* Gets the AST of the regular expression used in the test, if it can be seen locally.
*/
RegExpTerm getRegExp() {
result = getRegExpOperand().getALocalSource().(DataFlow::RegExpCreationNode).getRoot()
or
result = range.getRegExpOperand(true).asExpr().(StringLiteral).asRegExp()
result = this.(RegExpTest::Range).getRegExpOperand(true).asExpr().(StringLiteral).asRegExp()
}

/**
* Gets the data flow node corresponding to the regular expression object used in the test.
*
* In some cases this represents a string value being coerced to a RegExp object.
*/
DataFlow::Node getRegExpOperand() { result = range.getRegExpOperand(_) }
DataFlow::Node getRegExpOperand() { result = this.(RegExpTest::Range).getRegExpOperand(_) }

/**
* Gets the data flow node corresponding to the string being tested against the regular expression.
*/
DataFlow::Node getStringOperand() { result = range.getStringOperand() }
DataFlow::Node getStringOperand() { result = this.(RegExpTest::Range).getStringOperand() }

/**
* Gets the return value indicating that the string matched the regular expression.
*
* For example, for `regexp.exec(str) == null`, the polarity is `false`, and for
* `regexp.exec(str) != null` the polarity is `true`.
*/
boolean getPolarity() { result = range.getPolarity() }
boolean getPolarity() { result = this.(RegExpTest::Range).getPolarity() }
}

/**
Expand Down
Loading