-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathUntrustedCheckoutCritical.ql
More file actions
55 lines (52 loc) · 2.35 KB
/
UntrustedCheckoutCritical.ql
File metadata and controls
55 lines (52 loc) · 2.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* @name Checkout of untrusted code in a privileged 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 error
* @precision very-high
* @security-severity 9.3
* @id actions/untrusted-checkout/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() = checkoutTriggers() and
not exists(ControlCheck check | check.protects(checkout, event, "untrusted-checkout")) and
not exists(ControlCheck check | check.protects(poisonable, event, "untrusted-checkout"))
select checkout, checkout, poisonable,
"Potential execution of untrusted code on a privileged workflow ($@)", event, event.getName()