mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
refactor common code to identify untrusted checkouts
This commit is contained in:
@@ -92,28 +92,7 @@ class GitCommandSource extends RemoteFlowSource, CommandSource {
|
|||||||
|
|
||||||
GitCommandSource() {
|
GitCommandSource() {
|
||||||
exists(Step checkout, string cmd_regex |
|
exists(Step checkout, string cmd_regex |
|
||||||
// This should be:
|
checkout instanceof SimplePRHeadCheckoutStep and
|
||||||
// source instanceof PRHeadCheckoutStep
|
|
||||||
// but PRHeadCheckoutStep uses Taint Tracking anc causes a non-Monolitic Recursion error
|
|
||||||
// so we list all the subclasses of PRHeadCheckoutStep here and use actions/checkout as a workaround
|
|
||||||
// instead of using ActionsMutableRefCheckout and ActionsSHACheckout
|
|
||||||
(
|
|
||||||
exists(Uses uses |
|
|
||||||
checkout = uses and
|
|
||||||
uses.getCallee() = "actions/checkout" and
|
|
||||||
exists(uses.getArgument("ref")) and
|
|
||||||
not uses.getArgument("ref").matches("%base%") and
|
|
||||||
uses.getATriggerEvent().getName() = checkoutTriggers()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
checkout instanceof GitMutableRefCheckout
|
|
||||||
or
|
|
||||||
checkout instanceof GitSHACheckout
|
|
||||||
or
|
|
||||||
checkout instanceof GhMutableRefCheckout
|
|
||||||
or
|
|
||||||
checkout instanceof GhSHACheckout
|
|
||||||
) and
|
|
||||||
this.asExpr() = run.getScript() and
|
this.asExpr() = run.getScript() and
|
||||||
checkout.getAFollowingStep() = run and
|
checkout.getAFollowingStep() = run and
|
||||||
run.getScript().getAStmt() = cmd and
|
run.getScript().getAStmt() = cmd and
|
||||||
@@ -255,29 +234,7 @@ class ArtifactSource extends RemoteFlowSource, FileSource {
|
|||||||
private class CheckoutSource extends RemoteFlowSource, FileSource {
|
private class CheckoutSource extends RemoteFlowSource, FileSource {
|
||||||
Event event;
|
Event event;
|
||||||
|
|
||||||
CheckoutSource() {
|
CheckoutSource() { this.asExpr() instanceof SimplePRHeadCheckoutStep }
|
||||||
// This should be:
|
|
||||||
// source instanceof PRHeadCheckoutStep
|
|
||||||
// but PRHeadCheckoutStep uses Taint Tracking anc causes a non-Monolitic Recursion error
|
|
||||||
// so we list all the subclasses of PRHeadCheckoutStep here and use actions/checkout as a workaround
|
|
||||||
// instead of using ActionsMutableRefCheckout and ActionsSHACheckout
|
|
||||||
exists(Uses uses |
|
|
||||||
this.asExpr() = uses and
|
|
||||||
uses.getCallee() = "actions/checkout" and
|
|
||||||
exists(uses.getArgument("ref")) and
|
|
||||||
not uses.getArgument("ref").matches("%base%") and
|
|
||||||
event = uses.getATriggerEvent() and
|
|
||||||
event.getName() = checkoutTriggers()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
this.asExpr() instanceof GitMutableRefCheckout
|
|
||||||
or
|
|
||||||
this.asExpr() instanceof GitSHACheckout
|
|
||||||
or
|
|
||||||
this.asExpr() instanceof GhMutableRefCheckout
|
|
||||||
or
|
|
||||||
this.asExpr() instanceof GhSHACheckout
|
|
||||||
}
|
|
||||||
|
|
||||||
override string getSourceType() { result = "artifact" }
|
override string getSourceType() { result = "artifact" }
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ private import codeql.actions.TaintTracking
|
|||||||
import codeql.actions.DataFlow
|
import codeql.actions.DataFlow
|
||||||
import codeql.actions.dataflow.FlowSources
|
import codeql.actions.dataflow.FlowSources
|
||||||
import codeql.actions.security.PoisonableSteps
|
import codeql.actions.security.PoisonableSteps
|
||||||
|
import codeql.actions.security.UntrustedCheckoutQuery
|
||||||
|
|
||||||
string unzipRegexp() { result = "(unzip|tar)\\s+.*" }
|
string unzipRegexp() { result = "(unzip|tar)\\s+.*" }
|
||||||
|
|
||||||
@@ -22,11 +23,10 @@ class GitHubDownloadArtifactActionStep extends UntrustedArtifactDownloadStep, Us
|
|||||||
exists(this.getArgument("github-token"))
|
exists(this.getArgument("github-token"))
|
||||||
or
|
or
|
||||||
// There is an artifact upload step in the same workflow which can be influenced by an attacker on a checkout step
|
// There is an artifact upload step in the same workflow which can be influenced by an attacker on a checkout step
|
||||||
exists(LocalJob job, UsesStep checkout, UsesStep upload |
|
exists(LocalJob job, SimplePRHeadCheckoutStep checkout, UsesStep upload |
|
||||||
this.getEnclosingWorkflow().getAJob() = job and
|
this.getEnclosingWorkflow().getAJob() = job and
|
||||||
job.getAStep() = checkout and
|
job.getAStep() = checkout and
|
||||||
job.getATriggerEvent().getName() = "pull_request_target" and
|
checkout.getATriggerEvent().getName() = "pull_request_target" and
|
||||||
checkout.getCallee() = "actions/checkout" and
|
|
||||||
checkout.getAFollowingStep() = upload and
|
checkout.getAFollowingStep() = upload and
|
||||||
upload.getCallee() = "actions/upload-artifact"
|
upload.getCallee() = "actions/upload-artifact"
|
||||||
)
|
)
|
||||||
@@ -55,8 +55,10 @@ class DownloadArtifactActionStep extends UntrustedArtifactDownloadStep, UsesStep
|
|||||||
"ma-ve/action-download-artifact-with-retry"
|
"ma-ve/action-download-artifact-with-retry"
|
||||||
] and
|
] and
|
||||||
(
|
(
|
||||||
not exists(this.getArgument(["branch", "branch_name"])) or
|
not exists(this.getArgument(["branch", "branch_name"]))
|
||||||
not this.getArgument(["branch", "branch_name"]) = ["main", "master"]
|
or
|
||||||
|
exists(this.getArgument(["branch", "branch_name"])) and
|
||||||
|
this.getArgument("allow_forks") = "true"
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
not exists(this.getArgument(["commit", "commitHash", "commit_sha"])) or
|
not exists(this.getArgument(["commit", "commitHash", "commit_sha"])) or
|
||||||
@@ -74,7 +76,8 @@ class DownloadArtifactActionStep extends UntrustedArtifactDownloadStep, UsesStep
|
|||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
not exists(this.getArgument("pr")) or
|
not exists(this.getArgument("pr")) or
|
||||||
not this.getArgument("pr").matches("%github.event.pull_request.number%")
|
not this.getArgument("pr")
|
||||||
|
.matches(["%github.event.pull_request.number%", "%github.event.number%"])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,26 +20,7 @@ class OutputClobberingFromFileReadSink extends OutputClobberingSink {
|
|||||||
(
|
(
|
||||||
step instanceof UntrustedArtifactDownloadStep
|
step instanceof UntrustedArtifactDownloadStep
|
||||||
or
|
or
|
||||||
// This should be:
|
step instanceof SimplePRHeadCheckoutStep
|
||||||
// artifact instanceof PRHeadCheckoutStep
|
|
||||||
// but PRHeadCheckoutStep uses Taint Tracking anc causes a non-Monolitic Recursion error
|
|
||||||
// so we list all the subclasses of PRHeadCheckoutStep here and use actions/checkout as a workaround
|
|
||||||
// instead of using ActionsMutableRefCheckout and ActionsSHACheckout
|
|
||||||
exists(Uses uses |
|
|
||||||
step = uses and
|
|
||||||
uses.getCallee() = "actions/checkout" and
|
|
||||||
exists(uses.getArgument("ref")) and
|
|
||||||
not uses.getArgument("ref").matches("%base%") and
|
|
||||||
uses.getATriggerEvent().getName() = checkoutTriggers()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
step instanceof GitMutableRefCheckout
|
|
||||||
or
|
|
||||||
step instanceof GitSHACheckout
|
|
||||||
or
|
|
||||||
step instanceof GhMutableRefCheckout
|
|
||||||
or
|
|
||||||
step instanceof GhSHACheckout
|
|
||||||
) and
|
) and
|
||||||
step.getAFollowingStep() = run and
|
step.getAFollowingStep() = run and
|
||||||
this.asExpr() = run.getScript() and
|
this.asExpr() = run.getScript() and
|
||||||
|
|||||||
@@ -193,6 +193,31 @@ predicate containsHeadRef(string s) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SimplePRHeadCheckoutStep extends Step {
|
||||||
|
SimplePRHeadCheckoutStep() {
|
||||||
|
// This should be:
|
||||||
|
// artifact instanceof PRHeadCheckoutStep
|
||||||
|
// but PRHeadCheckoutStep uses Taint Tracking anc causes a non-Monolitic Recursion error
|
||||||
|
// so we list all the subclasses of PRHeadCheckoutStep here and use actions/checkout as a workaround
|
||||||
|
// instead of using ActionsMutableRefCheckout and ActionsSHACheckout
|
||||||
|
exists(Uses uses |
|
||||||
|
this = uses and
|
||||||
|
uses.getCallee() = "actions/checkout" and
|
||||||
|
exists(uses.getArgument("ref")) and
|
||||||
|
not uses.getArgument("ref").matches("%base%") and
|
||||||
|
uses.getATriggerEvent().getName() = checkoutTriggers()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
this instanceof GitMutableRefCheckout
|
||||||
|
or
|
||||||
|
this instanceof GitSHACheckout
|
||||||
|
or
|
||||||
|
this instanceof GhMutableRefCheckout
|
||||||
|
or
|
||||||
|
this instanceof GhSHACheckout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Checkout of a Pull Request HEAD */
|
/** Checkout of a Pull Request HEAD */
|
||||||
abstract class PRHeadCheckoutStep extends Step {
|
abstract class PRHeadCheckoutStep extends Step {
|
||||||
abstract string getPath();
|
abstract string getPath();
|
||||||
|
|||||||
Reference in New Issue
Block a user