initial commit of actions
This commit is contained in:
commit
949ece5785
44660 changed files with 12034344 additions and 0 deletions
45
github/codeql-action-v1/queries/binary-planting.ql
Normal file
45
github/codeql-action-v1/queries/binary-planting.ql
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* @name Exec call vulnerable to binary planting
|
||||
* @description On Windows, executing a binary with an unqualified name will execute a binary in the working directory in preference to a binary on PATH.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @id javascript/codeql-action/binary-planting
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class SafeWhichBarrierGuardNode extends DataFlow::BarrierGuardNode, DataFlow::InvokeNode {
|
||||
SafeWhichBarrierGuardNode() { getCalleeName() = "safeWhich" }
|
||||
|
||||
override predicate blocks(boolean outcome, Expr e) {
|
||||
outcome = true and
|
||||
e = getArgument(0).asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryPlantingConfiguration extends DataFlow::Configuration {
|
||||
BinaryPlantingConfiguration() {
|
||||
this = "BinaryPlantingConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(Node node) {
|
||||
node.asExpr() instanceof StringLiteral and
|
||||
not node.asExpr().(StringLiteral).getValue().matches("%/%") and
|
||||
not node.getFile().getBaseName().matches("%.test.ts")
|
||||
}
|
||||
|
||||
override predicate isSink(Node node) {
|
||||
node instanceof SystemCommandExecution or
|
||||
exists(InvokeExpr e | e.getCalleeName() = "ToolRunner" and e.getArgument(0) = node.asExpr())
|
||||
}
|
||||
|
||||
override predicate isBarrierGuard(DataFlow::BarrierGuardNode guard) {
|
||||
guard instanceof SafeWhichBarrierGuardNode
|
||||
}
|
||||
}
|
||||
|
||||
from BinaryPlantingConfiguration cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
select source.getNode(), source, sink, "This exec call might be vulnerable to Windows binary planting vulnerabilities."
|
||||
24
github/codeql-action-v1/queries/import-action-entrypoint.ql
Normal file
24
github/codeql-action-v1/queries/import-action-entrypoint.ql
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* @name Import action entrypoint
|
||||
* @description Importing the entrypoint file for an action is dangerous
|
||||
* because the code from that action will be run when the file is imported.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id javascript/codeql-action/import-action-entrypoint
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
class ActionEntrypointFile extends File {
|
||||
ActionEntrypointFile() {
|
||||
exists(Module m | m.getPath() = this.getAbsolutePath() and
|
||||
// This is quite a broad check and relies on the function name, but hopefully it'll be accurate enough
|
||||
m.getAStmt().getAChildExpr+().(CallExpr).getCalleeName() = "run") and
|
||||
// Requiring the relative path to exist limits us to files in the code repository and avoid libraries
|
||||
exists(this.getRelativePath())
|
||||
}
|
||||
}
|
||||
|
||||
from ImportDeclaration i
|
||||
where exists(ActionEntrypointFile f | i.getImportedModule().getPath() = f.getAbsolutePath())
|
||||
select i, "This imports the entrypoint file for an action. This will execute the code from the action."
|
||||
53
github/codeql-action-v1/queries/inconsistent-action-input.ql
Normal file
53
github/codeql-action-v1/queries/inconsistent-action-input.ql
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* @name Inconsistent action input
|
||||
* @description If multiple actions define an input with the same name, then the input
|
||||
* must be defined in an identical way to avoid confusion for the user.
|
||||
* This also makes writing queries like required-action-input.ql easier.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id javascript/codeql-action/inconsistent-action-input
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A declaration of a github action.
|
||||
*/
|
||||
class ActionDeclaration extends File {
|
||||
ActionDeclaration() {
|
||||
getRelativePath().matches("%/action.yml")
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the action.
|
||||
*/
|
||||
string getName() {
|
||||
result = getRelativePath().regexpCapture("(.*)/action.yml", 1)
|
||||
}
|
||||
|
||||
YAMLDocument getRootNode() {
|
||||
result.getFile() = this
|
||||
}
|
||||
|
||||
YAMLValue getInput(string inputName) {
|
||||
result = getRootNode().(YAMLMapping).lookup("inputs").(YAMLMapping).lookup(inputName)
|
||||
}
|
||||
}
|
||||
|
||||
predicate areNotEquivalent(YAMLValue x, YAMLValue y) {
|
||||
x.getTag() != y.getTag()
|
||||
or
|
||||
x.(YAMLScalar).getValue() != y.(YAMLScalar).getValue()
|
||||
or
|
||||
x.getNumChild() != y.getNumChild()
|
||||
or
|
||||
exists(int i | areNotEquivalent(x.getChild(i), y.getChild(i)))
|
||||
}
|
||||
|
||||
from ActionDeclaration actionA, ActionDeclaration actionB, string inputName
|
||||
where actionA.getName() < actionB.getName() // prevent duplicates which are permutations of the names
|
||||
and areNotEquivalent(actionA.getInput(inputName), actionB.getInput(inputName))
|
||||
// ram and threads inputs in different actions are supposed to have different description
|
||||
and inputName != "ram" and inputName != "threads"
|
||||
select actionA, "Action $@ and action $@ both declare input $@, however their definitions are not identical. This may be confusing to users.",
|
||||
actionA, actionA.getName(), actionB, actionB.getName(), inputName, inputName
|
||||
4
github/codeql-action-v1/queries/qlpack.yml
Normal file
4
github/codeql-action-v1/queries/qlpack.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
name: codeql-action-custom-queries-javascript
|
||||
version: 0.0.0
|
||||
libraryPathDependencies: codeql-javascript
|
||||
|
||||
97
github/codeql-action-v1/queries/required-action-input.ql
Normal file
97
github/codeql-action-v1/queries/required-action-input.ql
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* @name Required action input
|
||||
* @description For action inputs the core.input represents input with no value as the emptystring.
|
||||
* This doesn't promote good type checking. Instead, use either actions-util.getOptionalInput or
|
||||
* actions-util.getRequiredInput depending on if the input always has a value or not. The input
|
||||
* will always have a value if it is required or has a default value.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id javascript/codeql-action/required-action-input
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A declaration of a github action.
|
||||
*/
|
||||
class ActionDeclaration extends File {
|
||||
ActionDeclaration() {
|
||||
getRelativePath().matches("%/action.yml")
|
||||
}
|
||||
|
||||
YAMLDocument getRootNode() {
|
||||
result.getFile() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of any input to this action.
|
||||
*/
|
||||
string getAnInput() {
|
||||
result = getRootNode().(YAMLMapping).lookup("inputs").(YAMLMapping).getKey(_).(YAMLString).getValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* The given input always has a value, either because it is required,
|
||||
* or because it has a default value.
|
||||
*/
|
||||
predicate inputAlwaysHasValue(string input) {
|
||||
exists(YAMLMapping value |
|
||||
value = getRootNode().(YAMLMapping).lookup("inputs").(YAMLMapping).lookup(input) and
|
||||
(exists(value.lookup("default")) or
|
||||
value.lookup("required").(YAMLBool).getBoolValue() = true))
|
||||
}
|
||||
|
||||
/**
|
||||
* The function that is the entrypoint to this action.
|
||||
*/
|
||||
FunctionDeclStmt getEntrypoint() {
|
||||
result.getFile().getRelativePath() = getRootNode().
|
||||
(YAMLMapping).lookup("runs").
|
||||
(YAMLMapping).lookup("main").
|
||||
(YAMLString).getValue().regexpReplaceAll("\\.\\./lib/(.*)\\.js", "src/$1.ts") and
|
||||
result.getName() = "run"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An import from "@actions/core"
|
||||
*/
|
||||
class ActionsLibImport extends ImportDeclaration {
|
||||
ActionsLibImport() {
|
||||
getImportedPath().getValue() = "@actions/core"
|
||||
}
|
||||
|
||||
Variable getAProvidedVariable() {
|
||||
result = getASpecifier().getLocal().getVariable()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the core.getInput method.
|
||||
*/
|
||||
class CoreGetInputMethodCallExpr extends MethodCallExpr {
|
||||
CoreGetInputMethodCallExpr() {
|
||||
getMethodName() = "getInput" and
|
||||
exists(ActionsLibImport libImport |
|
||||
this.getReceiver() = libImport.getAProvidedVariable().getAnAccess() or
|
||||
this.getReceiver().(PropAccess).getBase() = libImport.getAProvidedVariable().getAnAccess())
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the input being accessed.
|
||||
*/
|
||||
string getInputName() {
|
||||
result = getArgument(0).(StringLiteral).getValue()
|
||||
}
|
||||
}
|
||||
|
||||
from ActionDeclaration action, CoreGetInputMethodCallExpr getInputCall, string inputName, string alternateFunction
|
||||
where action.getAnInput() = inputName
|
||||
// We don't want to create an alert for the users core.getInput in the getRequiredInput
|
||||
// and getOptionalInput functions themselves, and this check here does that in a
|
||||
// roundabout way by checking the parameter is a string literal. This should be enough
|
||||
// and hopefully won't discount any real calls to core.getInput, but is worth noting here.
|
||||
and getInputCall.getInputName() = inputName
|
||||
and ((action.inputAlwaysHasValue(inputName) and alternateFunction = "getRequiredInput")
|
||||
or (not action.inputAlwaysHasValue(inputName) and alternateFunction = "geOptionalInput"))
|
||||
select getInputCall, "This input may be undefined. Please use actions-util.$@ instead.", alternateFunction, alternateFunction
|
||||
103
github/codeql-action-v1/queries/undeclared-action-input.ql
Normal file
103
github/codeql-action-v1/queries/undeclared-action-input.ql
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* @name Undeclared action input
|
||||
* @description Code tries to use an input parameter that is not defined for this action.
|
||||
Perhaps this code is shared by multiple actions.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @id javascript/codeql-action/undeclared-action-input
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A declaration of a github action, including its inputs and entrypoint.
|
||||
*/
|
||||
class ActionDeclaration extends File {
|
||||
ActionDeclaration() {
|
||||
getRelativePath().matches("%/action.yml")
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the action.
|
||||
*/
|
||||
string getName() {
|
||||
result = getRelativePath().regexpCapture("(.*)/action.yml", 1)
|
||||
}
|
||||
|
||||
YAMLDocument getRootNode() {
|
||||
result.getFile() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of any input to this action.
|
||||
*/
|
||||
string getAnInput() {
|
||||
result = getRootNode().(YAMLMapping).lookup("inputs").(YAMLMapping).getKey(_).(YAMLString).getValue()
|
||||
}
|
||||
|
||||
/**
|
||||
* The function that is the entrypoint to this action.
|
||||
*/
|
||||
FunctionDeclStmt getEntrypoint() {
|
||||
result.getFile().getRelativePath() = getRootNode().
|
||||
(YAMLMapping).lookup("runs").
|
||||
(YAMLMapping).lookup("main").
|
||||
(YAMLString).getValue().regexpReplaceAll("\\.\\./lib/(.*)\\.js", "src/$1.ts") and
|
||||
result.getName() = "run"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function declared on CodeQL interface from codeql.ts
|
||||
*/
|
||||
class CodeQLFunction extends Function {
|
||||
CodeQLFunction() {
|
||||
exists(Function getCodeQLForCmd, ObjectExpr obj |
|
||||
getCodeQLForCmd.getName() = "getCodeQLForCmd" and
|
||||
obj = getCodeQLForCmd.getAStmt().(ReturnStmt).getExpr() and
|
||||
obj.getAProperty().getInit() = this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Any expr that is a transitive child of the given function.
|
||||
*/
|
||||
Expr getAFunctionChildExpr(Function f) {
|
||||
result.getContainer() = f
|
||||
}
|
||||
|
||||
/*
|
||||
* Result is a function that is called from the body of the given function `f`
|
||||
*/
|
||||
Function calledBy(Function f) {
|
||||
result = getAFunctionChildExpr(f).(InvokeExpr).getResolvedCallee()
|
||||
or
|
||||
// Assume outer function causes inner function to be called,
|
||||
// except for the special case of the CodeQL functions.
|
||||
(result.getEnclosingContainer() = f and not result instanceof CodeQLFunction)
|
||||
or
|
||||
// Handle calls to CodeQL functions by name
|
||||
getAFunctionChildExpr(f).(InvokeExpr).getCalleeName() = result.(CodeQLFunction).getName()
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the core.getInput method.
|
||||
*/
|
||||
class GetInputMethodCallExpr extends MethodCallExpr {
|
||||
GetInputMethodCallExpr() {
|
||||
getMethodName() = "getInput"
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the input being accessed.
|
||||
*/
|
||||
string getInputName() {
|
||||
result = getArgument(0).(StringLiteral).getValue()
|
||||
}
|
||||
}
|
||||
|
||||
from ActionDeclaration action, GetInputMethodCallExpr getInputCall, string inputName
|
||||
where getAFunctionChildExpr(calledBy*(action.getEntrypoint())) = getInputCall and
|
||||
inputName = getInputCall.getInputName() and
|
||||
not inputName = action.getAnInput()
|
||||
select getInputCall, "The $@ input is not defined for the $@ action", inputName, inputName, action, action.getName()
|
||||
Loading…
Add table
Add a link
Reference in a new issue