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

Sequester issue_comment triggered untrusted checkout from other triggers #18838

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 1 addition & 2 deletions actions/ql/lib/codeql/actions/security/ControlChecks.qll
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ string actor_not_attacker_event() {
// actor and attacker can be different
// actor may be a collaborator, but the attacker is may be the author of the PR that gets commented
// therefore it may be vulnerable to TOCTOU races where the actor reviews one thing and the attacker changes it
"issue_comment",
"pull_request_comment",
"pull_request_comment", "issue_comment"
]
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@
private import codeql.actions.TaintTracking

string checkoutTriggers() {
result = ["pull_request_target", "workflow_run", "workflow_call"]
}

string issueCommentTriggers() {
result = ["issue_comment"]

Check warning

Code scanning / CodeQL

Singleton set literal Warning

Singleton set literal can be replaced by its member.
}

string allCheckoutTriggers() {
result = ["pull_request_target", "workflow_run", "workflow_call", "issue_comment"]
}

Expand Down Expand Up @@ -77,7 +85,7 @@
private module ActionsSHACheckoutConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source.asExpr().getATriggerEvent().getName() =
["pull_request_target", "workflow_run", "workflow_call", "issue_comment"] and
allCheckoutTriggers() and
(
// `ref` argument contains the PR head/merge commit sha
exists(Expression e |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Description

GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the the trigger `pull_request_target` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.

## Recommendations

Expand Down
2 changes: 1 addition & 1 deletion actions/ql/src/Security/CWE-829/UntrustedCheckoutHigh.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Description

GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the triggers such as `pull_request_target` or `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.
GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the trigger `pull_request_target` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.

## Recommendations

Expand Down
18 changes: 1 addition & 17 deletions actions/ql/src/Security/CWE-829/UntrustedCheckoutHigh.ql
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,6 @@ where
// the checkout occurs in a privileged context
inPrivilegedContext(checkout, event) and
event.getName() = checkoutTriggers() and
(
// issue_comment: check for date comparison checks and actor/access control checks
event.getName() = "issue_comment" and
not exists(ControlCheck check, CommentVsHeadDateCheck date_check |
(
check instanceof ActorCheck or
check instanceof AssociationCheck or
check instanceof PermissionCheck
) and
check.dominates(checkout) and
date_check.dominates(checkout)
)
or
// not issue_comment triggered workflows
not event.getName() = "issue_comment" and
not exists(ControlCheck check | check.protects(checkout, event, "untrusted-checkout"))
)
not exists(ControlCheck check | check.protects(checkout, event, "untrusted-checkout"))
select checkout, "Potential execution of untrusted code on a privileged workflow ($@)", event,
event.getName()
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Execution of Untrusted Checked-out Code Triggered by Issue Comment

## Description

GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the the trigger `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.

## Recommendations

- Avoid using `issue_comment` unless necessary, consider using label gates instead to trigger workflows.
- If you must use `issue_comment` to trigger workflow runs, consider limiting the token that forked repos can use to read-only access to the repository or disallowing forks entirely.

Issue comment triggers are vulnerable to abuse by malicious actors. Triggering workflow run that checkout code through `issue_comment` can allow a malicious actor to execute code in the time between a comment being posted and the workflow run being triggered. Issue comment events are also not subject to the same security checks or pull request approvals that `pull_request` events are.

- [How to Secure your GitHub Actions Workflows with CodeQL](https://github.blog/security/application-security/how-to-secure-your-github-actions-workflows-with-codeql/#issueoops-security-pitfalls-with-issue_comment-trigger)
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @name Checkout of untrusted code in trusted context
* @description Privileged workflows have read/write access to the base repository and access to secrets.
* By explicitly checking out and running the build script from a fork the untrusted code is running in an environment
* that is able to push to the base repository and to access secrets.
* @kind path-problem
* @problem.severity warning
* @precision high
* @security-severity 7.5
* @id actions/untrusted-checkout-issue-comment/critical
* @tags actions
* security
* external/cwe/cwe-829
*/

import actions
import codeql.actions.security.UntrustedCheckoutQuery
import codeql.actions.security.PoisonableSteps
import codeql.actions.security.ControlChecks

query predicate edges(Step a, Step b) { a.getNextStep() = b }

from PRHeadCheckoutStep checkout, PoisonableStep poisonable, Event event
where
// the checkout is followed by a known poisonable step
checkout.getAFollowingStep() = poisonable and
(
poisonable instanceof Run and
(
// Check if the poisonable step is a local script execution step
// and the path of the command or script matches the path of the downloaded artifact
isSubpath(poisonable.(LocalScriptExecutionRunStep).getPath(), checkout.getPath())
or
// Checking the path for non local script execution steps is very difficult
not poisonable instanceof LocalScriptExecutionRunStep
// Its not easy to extract the path from a non-local script execution step so skipping this check for now
// and isSubpath(poisonable.(Run).getWorkingDirectory(), checkout.getPath())
)
or
poisonable instanceof UsesStep and
(
not poisonable instanceof LocalActionUsesStep and
checkout.getPath() = "GITHUB_WORKSPACE/"
or
isSubpath(poisonable.(LocalActionUsesStep).getPath(), checkout.getPath())
)
) and
// the checkout occurs in a privileged context
inPrivilegedContext(poisonable, event) and
inPrivilegedContext(checkout, event) and
event.getName() = issueCommentTriggers() and
not exists(ControlCheck check | check.protects(checkout, event, "untrusted-checkout")) and
not exists(ControlCheck check | check.protects(poisonable, event, "untrusted-checkout"))
select poisonable, checkout, poisonable,
"Potential execution of untrusted code on a privileged workflow ($@)", event, event.getName()

Check warning

Code scanning / CodeQL

Alert message style violation Warning

Alert message should end with a full stop.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Execution of Untrusted Checked-out Code Triggered by Issue Comment

## Description

GitHub workflows can be triggered through various repository events, including incoming pull requests (PRs) or comments on Issues/PRs. A potentially dangerous misuse of the the trigger `issue_comment` followed by an explicit checkout of untrusted code (Pull Request HEAD) may lead to repository compromise if untrusted code gets executed in a privileged job.

## Recommendations

- Avoid using `issue_comment` unless necessary, consider using label gates instead to trigger workflows.
- If you must use `issue_comment` to trigger workflow runs, consider limiting the token that forked repos can use to read-only access to the repository or disallowing forks entirely.

Issue comment triggers are vulnerable to abuse by malicious actors. Triggering workflow run that checkout code through `issue_comment` can allow a malicious actor to execute code in the time between a comment being posted and the workflow run being triggered. Issue comment events are also not subject to the same security checks or pull request approvals that `pull_request` events are.

- [How to Secure your GitHub Actions Workflows with CodeQL](https://github.blog/security/application-security/how-to-secure-your-github-actions-workflows-with-codeql/#issueoops-security-pitfalls-with-issue_comment-trigger)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* @name Checkout of untrusted code in trusted context
* @description Privileged workflows have read/write access to the base repository and access to secrets.
* By explicitly checking out and running the build script from a fork the untrusted code is running in an environment
* that is able to push to the base repository and to access secrets.
* @kind problem
* @problem.severity warning
* @precision high
* @security-severity 7.5
* @id actions/untrusted-checkout-issue-comment/high
* @tags actions
* security
* external/cwe/cwe-829
*/

import actions
import codeql.actions.security.UntrustedCheckoutQuery
import codeql.actions.security.PoisonableSteps
import codeql.actions.security.ControlChecks

from PRHeadCheckoutStep checkout, Event event
where
// the checkout is NOT followed by a known poisonable step
not checkout.getAFollowingStep() instanceof PoisonableStep and
// the checkout occurs in a privileged context
inPrivilegedContext(checkout, event) and
event.getName() = issueCommentTriggers() and
not exists(ControlCheck check | check.protects(checkout, event, "untrusted-checkout"))
select checkout, "Potential execution of untrusted code on a privileged workflow ($@)", event,

Check warning

Code scanning / CodeQL

Alert message style violation Warning

Alert message should end with a full stop.
event.getName()
Loading
Loading