mirror of
https://github.com/github/codeql.git
synced 2026-05-01 11:45:14 +02:00
Merge branch 'main' into property-stringify
This commit is contained in:
@@ -93,6 +93,9 @@ module Actions {
|
||||
|
||||
/** Gets the value of the `if` field in this job, if any. */
|
||||
JobIf getIf() { result.getJob() = this }
|
||||
|
||||
/** Gets the value of the `runs-on` field in this job. */
|
||||
JobRunson getRunsOn() { result.getJob() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,6 +111,19 @@ module Actions {
|
||||
Job getJob() { result = job }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `runs-on` within a job.
|
||||
* See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on.
|
||||
*/
|
||||
class JobRunson extends YamlNode, YamlScalar {
|
||||
Job job;
|
||||
|
||||
JobRunson() { job.lookup("runs-on") = this }
|
||||
|
||||
/** Gets the step this field belongs to. */
|
||||
Job getJob() { result = job }
|
||||
}
|
||||
|
||||
/**
|
||||
* A step within an Actions job.
|
||||
* See https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idsteps.
|
||||
|
||||
@@ -70,7 +70,7 @@ class JsxElement extends JsxNode {
|
||||
override string getAPrimaryQlClass() { result = "JsxElement" }
|
||||
|
||||
/**
|
||||
* Holds if this JSX element is a HTML element.
|
||||
* Holds if this JSX element is an HTML element.
|
||||
* That is, the name starts with a lowercase letter.
|
||||
*/
|
||||
predicate isHtmlElement() { getName().regexpMatch("[a-z].*") }
|
||||
|
||||
@@ -164,14 +164,27 @@ module ModelInput {
|
||||
class TypeModel extends Unit {
|
||||
/**
|
||||
* Gets a data-flow node that is a source of the type `package;type`.
|
||||
*
|
||||
* This must not depend on API graphs, but ensures that an API node is generated for
|
||||
* the source.
|
||||
*/
|
||||
DataFlow::Node getASource(string package, string type) { none() }
|
||||
|
||||
/**
|
||||
* Gets a data flow node that is a sink of the type `package;type`,
|
||||
* Gets a data-flow node that is a sink of the type `package;type`,
|
||||
* usually because it is an argument passed to a parameter of that type.
|
||||
*
|
||||
* This must not depend on API graphs, but ensures that an API node is generated for
|
||||
* the sink.
|
||||
*/
|
||||
DataFlow::Node getASink(string package, string type) { none() }
|
||||
|
||||
/**
|
||||
* Gets an API node that is a source or sink of the type `package;type`.
|
||||
*
|
||||
* Unlike `getASource` and `getASink`, this may depend on API graphs.
|
||||
*/
|
||||
API::Node getAnApiNode(string package, string type) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -434,6 +447,8 @@ private API::Node getNodeFromType(string package, string type) {
|
||||
or
|
||||
result = any(TypeModelDefEntry e).getNodeForType(package, type)
|
||||
or
|
||||
result = any(TypeModel t).getAnApiNode(package, type)
|
||||
or
|
||||
result = Specific::getExtraNodeFromType(package, type)
|
||||
}
|
||||
|
||||
@@ -529,7 +544,7 @@ private API::Node getNodeFromSubPath(API::Node base, AccessPath subPath) {
|
||||
}
|
||||
|
||||
/** Gets the node identified by the given `(package, type, path)` tuple. */
|
||||
API::Node getNodeFromPath(string package, string type, AccessPath path) {
|
||||
private API::Node getNodeFromPath(string package, string type, AccessPath path) {
|
||||
result = getNodeFromPath(package, type, path, path.getNumToken())
|
||||
}
|
||||
|
||||
@@ -552,7 +567,9 @@ private predicate typeStep(API::Node pred, API::Node succ) {
|
||||
*
|
||||
* Unlike `getNodeFromPath`, the `path` may end with one or more call-site filters.
|
||||
*/
|
||||
Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path, int n) {
|
||||
private Specific::InvokeNode getInvocationFromPath(
|
||||
string package, string type, AccessPath path, int n
|
||||
) {
|
||||
result = Specific::getAnInvocationOf(getNodeFromPath(package, type, path, n))
|
||||
or
|
||||
result = getInvocationFromPath(package, type, path, n - 1) and
|
||||
@@ -560,7 +577,7 @@ Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPa
|
||||
}
|
||||
|
||||
/** Gets an invocation identified by the given `(package, type, path)` tuple. */
|
||||
Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path) {
|
||||
private Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path) {
|
||||
result = getInvocationFromPath(package, type, path, path.getNumToken())
|
||||
}
|
||||
|
||||
@@ -568,7 +585,7 @@ Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPa
|
||||
* Holds if `name` is a valid name for an access path token in the identifying access path.
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate isValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
private predicate isValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
name = ["Argument", "Parameter", "ReturnValue", "WithArity", "TypeVar"]
|
||||
or
|
||||
Specific::isExtraValidTokenNameInIdentifyingAccessPath(name)
|
||||
@@ -579,7 +596,7 @@ predicate isValidTokenNameInIdentifyingAccessPath(string name) {
|
||||
* in an identifying access path.
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate isValidNoArgumentTokenInIdentifyingAccessPath(string name) {
|
||||
private predicate isValidNoArgumentTokenInIdentifyingAccessPath(string name) {
|
||||
name = "ReturnValue"
|
||||
or
|
||||
Specific::isExtraValidNoArgumentTokenInIdentifyingAccessPath(name)
|
||||
@@ -590,7 +607,7 @@ predicate isValidNoArgumentTokenInIdentifyingAccessPath(string name) {
|
||||
* in an identifying access path.
|
||||
*/
|
||||
bindingset[name, argument]
|
||||
predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argument) {
|
||||
private predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argument) {
|
||||
name = ["Argument", "Parameter"] and
|
||||
argument.regexpMatch("(N-|-)?\\d+(\\.\\.((N-|-)?\\d+)?)?")
|
||||
or
|
||||
@@ -607,51 +624,61 @@ predicate isValidTokenArgumentInIdentifyingAccessPath(string name, string argume
|
||||
* Module providing access to the imported models in terms of API graph nodes.
|
||||
*/
|
||||
module ModelOutput {
|
||||
/**
|
||||
* Holds if a CSV source model contributed `source` with the given `kind`.
|
||||
*/
|
||||
API::Node getASourceNode(string kind) {
|
||||
exists(string package, string type, string path |
|
||||
sourceModel(package, type, path, kind) and
|
||||
result = getNodeFromPath(package, type, path)
|
||||
)
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if a CSV source model contributed `source` with the given `kind`.
|
||||
*/
|
||||
cached
|
||||
API::Node getASourceNode(string kind) {
|
||||
exists(string package, string type, string path |
|
||||
sourceModel(package, type, path, kind) and
|
||||
result = getNodeFromPath(package, type, path)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a CSV sink model contributed `sink` with the given `kind`.
|
||||
*/
|
||||
cached
|
||||
API::Node getASinkNode(string kind) {
|
||||
exists(string package, string type, string path |
|
||||
sinkModel(package, type, path, kind) and
|
||||
result = getNodeFromPath(package, type, path)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a relevant CSV summary exists for these parameters.
|
||||
*/
|
||||
cached
|
||||
predicate relevantSummaryModel(
|
||||
string package, string type, string path, string input, string output, string kind
|
||||
) {
|
||||
isRelevantPackage(package) and
|
||||
summaryModel(package, type, path, input, output, kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `baseNode` is an invocation identified by the `package,type,path` part of a summary row.
|
||||
*/
|
||||
cached
|
||||
predicate resolvedSummaryBase(
|
||||
string package, string type, string path, Specific::InvokeNode baseNode
|
||||
) {
|
||||
summaryModel(package, type, path, _, _, _) and
|
||||
baseNode = getInvocationFromPath(package, type, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is seen as an instance of `(package,type)` due to a type definition
|
||||
* contributed by a CSV model.
|
||||
*/
|
||||
cached
|
||||
API::Node getATypeNode(string package, string type) { result = getNodeFromType(package, type) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a CSV sink model contributed `sink` with the given `kind`.
|
||||
*/
|
||||
API::Node getASinkNode(string kind) {
|
||||
exists(string package, string type, string path |
|
||||
sinkModel(package, type, path, kind) and
|
||||
result = getNodeFromPath(package, type, path)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a relevant CSV summary exists for these parameters.
|
||||
*/
|
||||
predicate relevantSummaryModel(
|
||||
string package, string type, string path, string input, string output, string kind
|
||||
) {
|
||||
isRelevantPackage(package) and
|
||||
summaryModel(package, type, path, input, output, kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `baseNode` is an invocation identified by the `package,type,path` part of a summary row.
|
||||
*/
|
||||
predicate resolvedSummaryBase(
|
||||
string package, string type, string path, Specific::InvokeNode baseNode
|
||||
) {
|
||||
summaryModel(package, type, path, _, _, _) and
|
||||
baseNode = getInvocationFromPath(package, type, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is seen as an instance of `(package,type)` due to a type definition
|
||||
* contributed by a CSV model.
|
||||
*/
|
||||
API::Node getATypeNode(string package, string type) { result = getNodeFromType(package, type) }
|
||||
import Cached
|
||||
|
||||
/**
|
||||
* Gets an error message relating to an invalid CSV row in a model.
|
||||
|
||||
@@ -87,7 +87,7 @@ predicate isBadRegexpFilter(HtmlMatchingRegExp regexp, string msg) {
|
||||
not regexp.fillsCaptureGroup("<script>", group) and
|
||||
msg =
|
||||
"This regular expression only parses --> (capture group " + group +
|
||||
") and not --!> as a HTML comment end tag."
|
||||
") and not --!> as an HTML comment end tag."
|
||||
)
|
||||
or
|
||||
regexp.matches("<!-- foo -->") and
|
||||
|
||||
@@ -80,7 +80,7 @@ module HtmlSanitization {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a HTML-relevant character that is replaced by `chain`.
|
||||
* Gets an HTML-relevant character that is replaced by `chain`.
|
||||
*/
|
||||
private string getALikelyReplacedCharacter(StringReplaceCallSequence chain) {
|
||||
result = "\"" and
|
||||
|
||||
@@ -35,7 +35,7 @@ private DangerousPrefixSubstring getADangerousMatchedChar(EmptyReplaceRegExpTerm
|
||||
or
|
||||
result = t.getAMatchedString()
|
||||
or
|
||||
// A substring matched by some character class. This is only used to match the "word" part of a HTML tag (e.g. "iframe" in "<iframe").
|
||||
// A substring matched by some character class. This is only used to match the "word" part of an HTML tag (e.g. "iframe" in "<iframe").
|
||||
exists(NfaUtils::CharacterClass cc |
|
||||
cc = NfaUtils::getCanonicalCharClass(t) and
|
||||
cc.matches(result) and
|
||||
@@ -101,12 +101,12 @@ private class RepetitionMatcher extends EmptyReplaceRegExpTerm {
|
||||
predicate matchesDangerousPrefix(EmptyReplaceRegExpTerm t, string prefix, string kind) {
|
||||
prefix = getADangerousMatchedPrefix(t) and
|
||||
(
|
||||
kind = "path injection" and
|
||||
kind = "a path injection vulnerability" and
|
||||
prefix = ["/..", "../"] and
|
||||
// If the regex is matching explicit path components, it is unlikely that it's being used as a sanitizer.
|
||||
not t.getSuccessor*().getAMatchedString().regexpMatch("(?is).*[a-z0-9_-].*")
|
||||
or
|
||||
kind = "HTML element injection" and
|
||||
kind = "an HTML element injection vulnerability" and
|
||||
(
|
||||
// comments
|
||||
prefix = "<!--" and
|
||||
@@ -119,7 +119,7 @@ predicate matchesDangerousPrefix(EmptyReplaceRegExpTerm t, string prefix, string
|
||||
)
|
||||
)
|
||||
or
|
||||
kind = "HTML attribute injection" and
|
||||
kind = "an HTML attribute injection vulnerability" and
|
||||
prefix =
|
||||
[
|
||||
// ordinary event handler prefix
|
||||
@@ -197,6 +197,6 @@ query predicate problems(
|
||||
) {
|
||||
exists(string kind |
|
||||
isResult(replace, dangerous, prefix, kind) and
|
||||
msg = "This string may still contain $@, which may cause a " + kind + " vulnerability."
|
||||
msg = "This string may still contain $@, which may cause " + kind + "."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -213,6 +213,9 @@ module PasswordHeuristics {
|
||||
normalized
|
||||
.regexpMatch(".*(pass|test|sample|example|secret|root|admin|user|change|auth|fake|(my(token|password))|string|foo|bar|baz|qux|1234|3141|abcd).*")
|
||||
)
|
||||
or
|
||||
// repeats the same char more than 10 times
|
||||
password.regexpMatch(".*([a-zA-Z0-9])\\1{10,}.*")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,7 @@ module ImproperCodeSanitization {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A call to a HTML sanitizer seen as a source for improper code sanitization
|
||||
* A call to an HTML sanitizer seen as a source for improper code sanitization
|
||||
*/
|
||||
class HtmlSanitizerCallAsSource extends Source {
|
||||
HtmlSanitizerCallAsSource() { this instanceof HtmlSanitizerCall }
|
||||
|
||||
@@ -32,7 +32,7 @@ module UnsafeJQueryPlugin {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* An argument that may act as a HTML fragment rather than a CSS selector, as a sink for remote unsafe jQuery plugins.
|
||||
* An argument that may act as an HTML fragment rather than a CSS selector, as a sink for remote unsafe jQuery plugins.
|
||||
*/
|
||||
class AmbiguousHtmlOrSelectorArgument extends DataFlow::Node,
|
||||
DomBasedXss::JQueryHtmlOrSelectorArgument {
|
||||
@@ -173,7 +173,7 @@ module UnsafeJQueryPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument that may act as a HTML fragment rather than a CSS selector, as a sink for remote unsafe jQuery plugins.
|
||||
* An argument that may act as an HTML fragment rather than a CSS selector, as a sink for remote unsafe jQuery plugins.
|
||||
*/
|
||||
class AmbiguousHtmlOrSelectorArgumentAsSink extends Sink {
|
||||
AmbiguousHtmlOrSelectorArgumentAsSink() {
|
||||
@@ -182,7 +182,7 @@ module UnsafeJQueryPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* A hint that a value is expected to be treated as a HTML fragment later.
|
||||
* A hint that a value is expected to be treated as an HTML fragment later.
|
||||
*/
|
||||
class IntentionalHtmlFragmentHint extends Sanitizer {
|
||||
IntentionalHtmlFragmentHint() {
|
||||
@@ -191,7 +191,7 @@ module UnsafeJQueryPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a jQuery plugin that likely expects `sink` to be treated as a HTML fragment.
|
||||
* Holds if there exists a jQuery plugin that likely expects `sink` to be treated as an HTML fragment.
|
||||
*/
|
||||
predicate isLikelyIntentionalHtmlSink(DataFlow::Node sink) {
|
||||
exists(
|
||||
@@ -206,7 +206,7 @@ module UnsafeJQueryPlugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a property-write that writes a HTML-like constant string to `prop`.
|
||||
* Gets a property-write that writes an HTML-like constant string to `prop`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private DataFlow::PropWrite getALikelyHtmlWrite(string prop) {
|
||||
|
||||
@@ -65,7 +65,7 @@ module Shared {
|
||||
private import semmle.javascript.security.dataflow.IncompleteHtmlAttributeSanitizationCustomizations::IncompleteHtmlAttributeSanitization as IncompleteHtml
|
||||
|
||||
/**
|
||||
* A guard that checks if a string can contain quotes, which is a guard for strings that are inside a HTML attribute.
|
||||
* A guard that checks if a string can contain quotes, which is a guard for strings that are inside an HTML attribute.
|
||||
*/
|
||||
abstract class QuoteGuard extends TaintTracking::SanitizerGuardNode, StringOps::Includes {
|
||||
QuoteGuard() {
|
||||
|
||||
Reference in New Issue
Block a user