diff --git a/ql/lib/codeql/actions/Ast.qll b/ql/lib/codeql/actions/Ast.qll index c83abb1ea1d..a1651eedc47 100644 --- a/ql/lib/codeql/actions/Ast.qll +++ b/ql/lib/codeql/actions/Ast.qll @@ -79,8 +79,6 @@ class CompositeAction extends AstNode instanceof CompositeActionImpl { UsesStep getACallerStep() { result = super.getACallerStep() } predicate isPrivileged() { super.isPrivileged() } - - predicate isPrivilegedExternallyTriggerable() { super.isPrivilegedExternallyTriggerable() } } /** @@ -200,7 +198,9 @@ abstract class Job extends AstNode instanceof JobImpl { predicate isPrivileged() { super.isPrivileged() } - predicate isPrivilegedExternallyTriggerable() { super.isPrivilegedExternallyTriggerable() } + predicate isPrivilegedExternallyTriggerable(Event event) { + super.isPrivilegedExternallyTriggerable(event) + } } abstract class StepsContainer extends AstNode instanceof StepsContainerImpl { diff --git a/ql/lib/codeql/actions/Helper.qll b/ql/lib/codeql/actions/Helper.qll index 0df7b125019..9356950f571 100644 --- a/ql/lib/codeql/actions/Helper.qll +++ b/ql/lib/codeql/actions/Helper.qll @@ -238,44 +238,12 @@ predicate fileToGitHubPath(Run run, string path) { fileToFileWrite(run.getScript(), "GITHUB_PATH", path) } -predicate inPrivilegedCompositeAction(AstNode node) { - exists(CompositeAction a | - a = node.getEnclosingCompositeAction() and - a.isPrivilegedExternallyTriggerable() - ) -} - -predicate inPrivilegedExternallyTriggerableJob(AstNode node) { - exists(Job j | - j = node.getEnclosingJob() and - j.isPrivilegedExternallyTriggerable() - ) -} - -predicate inPrivilegedContext(AstNode node) { - inPrivilegedCompositeAction(node) - or - inPrivilegedExternallyTriggerableJob(node) -} - -predicate inNonPrivilegedCompositeAction(AstNode node) { - exists(CompositeAction a | - a = node.getEnclosingCompositeAction() and - not a.isPrivilegedExternallyTriggerable() - ) -} - -predicate inNonPrivilegedJob(AstNode node) { - exists(Job j | - j = node.getEnclosingJob() and - not j.isPrivilegedExternallyTriggerable() - ) +predicate inPrivilegedContext(AstNode node, Event event) { + node.getEnclosingJob().isPrivilegedExternallyTriggerable(event) } predicate inNonPrivilegedContext(AstNode node) { - inNonPrivilegedCompositeAction(node) - or - inNonPrivilegedJob(node) + not node.getEnclosingJob().isPrivilegedExternallyTriggerable(_) } string partialFileContentRegexp() { diff --git a/ql/lib/codeql/actions/ast/internal/Ast.qll b/ql/lib/codeql/actions/ast/internal/Ast.qll index 7659661bdac..154d466ab7d 100644 --- a/ql/lib/codeql/actions/ast/internal/Ast.qll +++ b/ql/lib/codeql/actions/ast/internal/Ast.qll @@ -359,18 +359,6 @@ class CompositeActionImpl extends AstNodeImpl, TCompositeAction { } EventImpl getATriggerEvent() { result = this.getACallerJob().getATriggerEvent() } - - /** Holds if the action is privileged and externally triggerable. */ - predicate isPrivilegedExternallyTriggerable() { - // the action is externally triggerable - exists(JobImpl caller, EventImpl event | - caller = this.getACallerJob() and - event = caller.getATriggerEvent() and - event.isExternallyTriggerable() and - // the action is privileged - (this.isPrivileged() or caller.isPrivileged()) - ) - } } class WorkflowImpl extends AstNodeImpl, TWorkflowNode { @@ -970,31 +958,30 @@ class JobImpl extends AstNodeImpl, TJobNode { } /** Holds if the action is privileged and externally triggerable. */ - predicate isPrivilegedExternallyTriggerable() { - exists(EventImpl e | this.getATriggerEvent() = e | - // job is triggereable by an external user - e.isExternallyTriggerable() and - // no matter if `pull_request` is granted write permissions or access to secrets - // when the job is triggered by a `pull_request` event from a fork, they will get revoked - not e.getName() = "pull_request" and - ( - // job is privileged (write access or access to secrets) - this.isPrivileged() - or - // the trigger event is __normally__ privileged - e.isPrivileged() and - // and we have no runtime data to prove otherwise - not this.hasRuntimeData() and - // and the job is not explicitly non-privileged - not ( - ( - this.hasExplicitNonePermission() or - this.hasImplicitNonePermission() or - this.hasExplicitReadPermission() or - this.hasImplicitReadPermission() - ) and - not this.hasExplicitSecretAccess() - ) + predicate isPrivilegedExternallyTriggerable(EventImpl event) { + this.getATriggerEvent() = event and + // job is triggereable by an external user + event.isExternallyTriggerable() and + // no matter if `pull_request` is granted write permissions or access to secrets + // when the job is triggered by a `pull_request` event from a fork, they will get revoked + not event.getName() = "pull_request" and + ( + // job is privileged (write access or access to secrets) + this.isPrivileged() + or + // the trigger event is __normally__ privileged + event.isPrivileged() and + // and we have no runtime data to prove otherwise + not this.hasRuntimeData() and + // and the job is not explicitly non-privileged + not ( + ( + this.hasExplicitNonePermission() or + this.hasImplicitNonePermission() or + this.hasExplicitReadPermission() or + this.hasImplicitReadPermission() + ) and + not this.hasExplicitSecretAccess() ) ) } @@ -1073,6 +1060,12 @@ class StepImpl extends AstNodeImpl, TStepNode { override YamlMapping getNode() { result = n } + override JobImpl getEnclosingJob() { + // if a step is within a composite action, we should follow the caller job + result = this.getEnclosingCompositeAction().getACallerJob() or + result = super.getEnclosingJob() + } + EnvImpl getEnv() { result.getNode() = n.lookup("env") } /** Gets the ID of this step, if any. */ diff --git a/ql/lib/codeql/actions/security/ControlChecks.qll b/ql/lib/codeql/actions/security/ControlChecks.qll index 1a3e1e15fe8..052b22cd338 100644 --- a/ql/lib/codeql/actions/security/ControlChecks.qll +++ b/ql/lib/codeql/actions/security/ControlChecks.qll @@ -38,9 +38,12 @@ abstract class ControlCheck extends AstNode { } predicate protects(Step step, Event event, string category) { - event = step.getEnclosingWorkflow().getATriggerEvent() and + // The check dominates the step it should protect this.dominates(step) and - this.protectsCategoryAndEvent(category, event.getName()) + // The check is effective against the event and category + this.protectsCategoryAndEvent(category, event.getName()) and + // The check can be triggered by the event + this.getEnclosingJob().getATriggerEvent() = event } predicate dominates(Step step) { diff --git a/ql/src/Security/CWE-074/OutputClobberingHigh.ql b/ql/src/Security/CWE-074/OutputClobberingHigh.ql index 0ead5aa7689..2000e2100ae 100644 --- a/ql/src/Security/CWE-074/OutputClobberingHigh.ql +++ b/ql/src/Security/CWE-074/OutputClobberingHigh.ql @@ -18,25 +18,20 @@ import codeql.actions.dataflow.ExternalFlow import OutputClobberingFlow::PathGraph import codeql.actions.security.ControlChecks -from OutputClobberingFlow::PathNode source, OutputClobberingFlow::PathNode sink +from OutputClobberingFlow::PathNode source, OutputClobberingFlow::PathNode sink, Event event where OutputClobberingFlow::flowPath(source, sink) and - inPrivilegedContext(sink.getNode().asExpr()) and + inPrivilegedContext(sink.getNode().asExpr(), event) and // exclude paths to file read sinks from non-artifact sources ( not source.getNode().(RemoteFlowSource).getSourceType() = "artifact" and not exists(ControlCheck check | - check - .protects(sink.getNode().asExpr(), - source.getNode().asExpr().getEnclosingJob().getATriggerEvent(), "code-injection") + check.protects(sink.getNode().asExpr(), event, "code-injection") ) or source.getNode().(RemoteFlowSource).getSourceType() = "artifact" and not exists(ControlCheck check | - check - .protects(sink.getNode().asExpr(), - source.getNode().asExpr().getEnclosingJob().getATriggerEvent(), - ["untrusted-checkout", "artifact-poisoning"]) + check.protects(sink.getNode().asExpr(), event, ["untrusted-checkout", "artifact-poisoning"]) ) and ( sink.getNode() instanceof OutputClobberingFromFileReadSink or diff --git a/ql/src/Security/CWE-077/EnvPathInjectionCritical.ql b/ql/src/Security/CWE-077/EnvPathInjectionCritical.ql index 9fa066d195c..54e013f1091 100644 --- a/ql/src/Security/CWE-077/EnvPathInjectionCritical.ql +++ b/ql/src/Security/CWE-077/EnvPathInjectionCritical.ql @@ -17,24 +17,19 @@ import codeql.actions.security.EnvPathInjectionQuery import EnvPathInjectionFlow::PathGraph import codeql.actions.security.ControlChecks -from EnvPathInjectionFlow::PathNode source, EnvPathInjectionFlow::PathNode sink +from EnvPathInjectionFlow::PathNode source, EnvPathInjectionFlow::PathNode sink, Event event where EnvPathInjectionFlow::flowPath(source, sink) and - inPrivilegedContext(sink.getNode().asExpr()) and + inPrivilegedContext(sink.getNode().asExpr(), event) and ( not source.getNode().(RemoteFlowSource).getSourceType() = "artifact" and not exists(ControlCheck check | - check - .protects(sink.getNode().asExpr(), - source.getNode().asExpr().getEnclosingJob().getATriggerEvent(), "code-injection") + check.protects(sink.getNode().asExpr(), event, "code-injection") ) or source.getNode().(RemoteFlowSource).getSourceType() = "artifact" and not exists(ControlCheck check | - check - .protects(sink.getNode().asExpr(), - source.getNode().asExpr().getEnclosingJob().getATriggerEvent(), - ["untrusted-checkout", "artifact-poisoning"]) + check.protects(sink.getNode().asExpr(), event, ["untrusted-checkout", "artifact-poisoning"]) ) and sink.getNode() instanceof EnvPathInjectionFromFileReadSink ) diff --git a/ql/src/Security/CWE-077/EnvVarInjectionCritical.ql b/ql/src/Security/CWE-077/EnvVarInjectionCritical.ql index 806bae2a91d..b301915d79c 100644 --- a/ql/src/Security/CWE-077/EnvVarInjectionCritical.ql +++ b/ql/src/Security/CWE-077/EnvVarInjectionCritical.ql @@ -18,30 +18,23 @@ import codeql.actions.dataflow.ExternalFlow import EnvVarInjectionFlow::PathGraph import codeql.actions.security.ControlChecks -from EnvVarInjectionFlow::PathNode source, EnvVarInjectionFlow::PathNode sink +from EnvVarInjectionFlow::PathNode source, EnvVarInjectionFlow::PathNode sink, Event event where EnvVarInjectionFlow::flowPath(source, sink) and - inPrivilegedContext(sink.getNode().asExpr()) and + inPrivilegedContext(sink.getNode().asExpr(), event) and not exists(ControlCheck check | - check - .protects(sink.getNode().asExpr(), - source.getNode().asExpr().getEnclosingJob().getATriggerEvent(), "envvar-injection") + check.protects(sink.getNode().asExpr(), event, "envvar-injection") ) and // exclude paths to file read sinks from non-artifact sources ( not source.getNode().(RemoteFlowSource).getSourceType() = "artifact" and not exists(ControlCheck check | - check - .protects(sink.getNode().asExpr(), - source.getNode().asExpr().getEnclosingJob().getATriggerEvent(), "code-injection") + check.protects(sink.getNode().asExpr(), event, "code-injection") ) or source.getNode().(RemoteFlowSource).getSourceType() = "artifact" and not exists(ControlCheck check | - check - .protects(sink.getNode().asExpr(), - source.getNode().asExpr().getEnclosingJob().getATriggerEvent(), - ["untrusted-checkout", "artifact-poisoning"]) + check.protects(sink.getNode().asExpr(), event, ["untrusted-checkout", "artifact-poisoning"]) ) and ( sink.getNode() instanceof EnvVarInjectionFromFileReadSink or diff --git a/ql/src/Security/CWE-078/CommandInjectionCritical.ql b/ql/src/Security/CWE-078/CommandInjectionCritical.ql index f5a4aed3eca..80281e8db30 100644 --- a/ql/src/Security/CWE-078/CommandInjectionCritical.ql +++ b/ql/src/Security/CWE-078/CommandInjectionCritical.ql @@ -17,10 +17,10 @@ import actions import codeql.actions.security.CommandInjectionQuery import CommandInjectionFlow::PathGraph -from CommandInjectionFlow::PathNode source, CommandInjectionFlow::PathNode sink +from CommandInjectionFlow::PathNode source, CommandInjectionFlow::PathNode sink, Event event where CommandInjectionFlow::flowPath(source, sink) and - inPrivilegedContext(sink.getNode().asExpr()) + inPrivilegedContext(sink.getNode().asExpr(), event) select sink.getNode(), source, sink, "Potential command injection in $@, which may be controlled by an external user.", sink, sink.getNode().asExpr().(Expression).getRawExpression() diff --git a/ql/src/Security/CWE-088/ArgumentInjectionCritical.ql b/ql/src/Security/CWE-088/ArgumentInjectionCritical.ql index 6f1f6008a06..2626de31935 100644 --- a/ql/src/Security/CWE-088/ArgumentInjectionCritical.ql +++ b/ql/src/Security/CWE-088/ArgumentInjectionCritical.ql @@ -16,14 +16,12 @@ import codeql.actions.security.ArgumentInjectionQuery import ArgumentInjectionFlow::PathGraph import codeql.actions.security.ControlChecks -from ArgumentInjectionFlow::PathNode source, ArgumentInjectionFlow::PathNode sink +from ArgumentInjectionFlow::PathNode source, ArgumentInjectionFlow::PathNode sink, Event event where ArgumentInjectionFlow::flowPath(source, sink) and - inPrivilegedContext(sink.getNode().asExpr()) and + inPrivilegedContext(sink.getNode().asExpr(), event) and not exists(ControlCheck check | - check - .protects(sink.getNode().asExpr(), - source.getNode().asExpr().getEnclosingJob().getATriggerEvent(), "argument-injection") + check.protects(sink.getNode().asExpr(), event, "argument-injection") ) select sink.getNode(), source, sink, "Potential argument injection in $@ command, which may be controlled by an external user.", sink, diff --git a/ql/src/Security/CWE-094/CodeInjectionCritical.ql b/ql/src/Security/CWE-094/CodeInjectionCritical.ql index ec4925d24a0..ef66ac229f2 100644 --- a/ql/src/Security/CWE-094/CodeInjectionCritical.ql +++ b/ql/src/Security/CWE-094/CodeInjectionCritical.ql @@ -19,15 +19,11 @@ import codeql.actions.security.CodeInjectionQuery import CodeInjectionFlow::PathGraph import codeql.actions.security.ControlChecks -from CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink +from CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink, Event event where CodeInjectionFlow::flowPath(source, sink) and - inPrivilegedContext(sink.getNode().asExpr()) and - not exists(ControlCheck check | - check - .protects(sink.getNode().asExpr(), - source.getNode().asExpr().getEnclosingJob().getATriggerEvent(), "code-injection") - ) and + inPrivilegedContext(sink.getNode().asExpr(), event) and + not exists(ControlCheck check | check.protects(sink.getNode().asExpr(), event, "code-injection")) and // exclude cases where the sink is a JS script and the expression uses toJson not exists(UsesStep script | script.getCallee() = "actions/github-script" and diff --git a/ql/src/Security/CWE-349/CachePoisoningViaCodeInjection.ql b/ql/src/Security/CWE-349/CachePoisoningViaCodeInjection.ql index 67b615d115a..411d0052d4b 100644 --- a/ql/src/Security/CWE-349/CachePoisoningViaCodeInjection.ql +++ b/ql/src/Security/CWE-349/CachePoisoningViaCodeInjection.ql @@ -18,27 +18,27 @@ import codeql.actions.security.CachePoisoningQuery import CodeInjectionFlow::PathGraph import codeql.actions.security.ControlChecks -from CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink, LocalJob j, Event e +from CodeInjectionFlow::PathNode source, CodeInjectionFlow::PathNode sink, LocalJob job, Event event where CodeInjectionFlow::flowPath(source, sink) and - j = sink.getNode().asExpr().getEnclosingJob() and - j.getATriggerEvent() = e and + job = sink.getNode().asExpr().getEnclosingJob() and + job.getATriggerEvent() = event and // job can be triggered by an external user - e.isExternallyTriggerable() and + event.isExternallyTriggerable() and // the checkout is not controlled by an access check not exists(ControlCheck check | - check.protects(source.getNode().asExpr(), j.getATriggerEvent(), "code-injection") + check.protects(source.getNode().asExpr(), event, "code-injection") ) and // excluding privileged workflows since they can be exploited in easier circumstances - not j.isPrivileged() and + not job.isPrivileged() and ( // the workflow runs in the context of the default branch - runsOnDefaultBranch(e) + runsOnDefaultBranch(event) or // the workflow caller runs in the context of the default branch - e.getName() = "workflow_call" and + event.getName() = "workflow_call" and exists(ExternalJob caller | - caller.getCallee() = j.getLocation().getFile().getRelativePath() and + caller.getCallee() = job.getLocation().getFile().getRelativePath() and runsOnDefaultBranch(caller.getATriggerEvent()) ) ) diff --git a/ql/src/Security/CWE-349/CachePoisoningViaDirectCache.ql b/ql/src/Security/CWE-349/CachePoisoningViaDirectCache.ql index b6df022329d..bda8224925e 100644 --- a/ql/src/Security/CWE-349/CachePoisoningViaDirectCache.ql +++ b/ql/src/Security/CWE-349/CachePoisoningViaDirectCache.ql @@ -43,10 +43,10 @@ predicate controlledCachePath(string cache_path, string untrusted_path) { query predicate edges(Step a, Step b) { a.getNextStep() = b } -from LocalJob j, Event e, Step source, Step s, string message, string path +from LocalJob job, Event event, Step source, Step step, string message, string path where // the job checkouts untrusted code from a pull request or downloads an untrusted artifact - j.getAStep() = source and + job.getAStep() = source and ( source instanceof PRHeadCheckoutStep and message = "due to privilege checkout of untrusted code." and @@ -58,35 +58,36 @@ where ) and // the checkout/download is not controlled by an access check not exists(ControlCheck check | - check.protects(source, j.getATriggerEvent(), ["untrusted-checkout", "artifact-poisoning"]) + check.protects(source, event, ["untrusted-checkout", "artifact-poisoning"]) ) and - j.getATriggerEvent() = e and + job.getATriggerEvent() = event and // job can be triggered by an external user - e.isExternallyTriggerable() and + event.isExternallyTriggerable() and ( // the workflow runs in the context of the default branch - runsOnDefaultBranch(e) + runsOnDefaultBranch(event) or // the workflow's caller runs in the context of the default branch - e.getName() = "workflow_call" and + event.getName() = "workflow_call" and exists(ExternalJob caller | - caller.getCallee() = j.getLocation().getFile().getRelativePath() and + caller.getCallee() = job.getLocation().getFile().getRelativePath() and runsOnDefaultBranch(caller.getATriggerEvent()) ) ) and // the job writes to the cache // (No need to follow the checkout/download step since the cache is normally write after the job completes) - j.getAStep() = s and - s instanceof CacheWritingStep and + job.getAStep() = step and + step instanceof CacheWritingStep and ( // we dont know what code can be controlled by the attacker path = "?" or // we dont know what files are being cached - s.(CacheWritingStep).getPath() = "?" + step.(CacheWritingStep).getPath() = "?" or // the cache writing step reads from a path the attacker can control - not path = "?" and controlledCachePath(s.(CacheWritingStep).getPath(), path) + not path = "?" and controlledCachePath(step.(CacheWritingStep).getPath(), path) ) and - not s instanceof PoisonableStep -select s, source, s, "Potential cache poisoning in the context of the default branch " + message + not step instanceof PoisonableStep +select step, source, step, + "Potential cache poisoning in the context of the default branch " + message diff --git a/ql/src/Security/CWE-349/CachePoisoningViaPoisonableStep.ql b/ql/src/Security/CWE-349/CachePoisoningViaPoisonableStep.ql index 0750a02930e..74f49fccd30 100644 --- a/ql/src/Security/CWE-349/CachePoisoningViaPoisonableStep.ql +++ b/ql/src/Security/CWE-349/CachePoisoningViaPoisonableStep.ql @@ -20,10 +20,10 @@ import codeql.actions.security.ControlChecks query predicate edges(Step a, Step b) { a.getNextStep() = b } -from LocalJob j, Event e, Step source, Step s, string message, string path +from LocalJob job, Event event, Step source, Step step, string message, string path where // the job checkouts untrusted code from a pull request or downloads an untrusted artifact - j.getAStep() = source and + job.getAStep() = source and ( source instanceof PRHeadCheckoutStep and message = "due to privilege checkout of untrusted code." and @@ -35,26 +35,27 @@ where ) and // the checkout/download is not controlled by an access check not exists(ControlCheck check | - check.protects(source, j.getATriggerEvent(), ["untrusted-checkout", "artifact-poisoning"]) + check.protects(source, event, ["untrusted-checkout", "artifact-poisoning"]) ) and - j.getATriggerEvent() = e and + job.getATriggerEvent() = event and // job can be triggered by an external user - e.isExternallyTriggerable() and + event.isExternallyTriggerable() and ( // the workflow runs in the context of the default branch - runsOnDefaultBranch(e) + runsOnDefaultBranch(event) or // the workflow's caller runs in the context of the default branch - e.getName() = "workflow_call" and + event.getName() = "workflow_call" and exists(ExternalJob caller | - caller.getCallee() = j.getLocation().getFile().getRelativePath() and + caller.getCallee() = job.getLocation().getFile().getRelativePath() and runsOnDefaultBranch(caller.getATriggerEvent()) ) ) and // the job executes checked-out code // (The cache specific token can be leaked even for non-privileged workflows) - source.getAFollowingStep() = s and - s instanceof PoisonableStep and + source.getAFollowingStep() = step and + step instanceof PoisonableStep and // excluding privileged workflows since they can be exploited in easier circumstances - not j.isPrivileged() -select s, source, s, "Potential cache poisoning in the context of the default branch " + message + not job.isPrivileged() +select step, source, step, + "Potential cache poisoning in the context of the default branch " + message diff --git a/ql/src/Security/CWE-367/UntrustedCheckoutTOCTOUCritical.ql b/ql/src/Security/CWE-367/UntrustedCheckoutTOCTOUCritical.ql index 7c7ab15de31..11897c464bf 100644 --- a/ql/src/Security/CWE-367/UntrustedCheckoutTOCTOUCritical.ql +++ b/ql/src/Security/CWE-367/UntrustedCheckoutTOCTOUCritical.ql @@ -18,16 +18,18 @@ import codeql.actions.security.ControlChecks query predicate edges(Step a, Step b) { a.getNextStep() = b } -from LocalJob j, MutableRefCheckoutStep checkout, PoisonableStep s, ControlCheck check +from + LocalJob job, MutableRefCheckoutStep checkout, PoisonableStep step, ControlCheck check, + Event event where - j.getAStep() = checkout and + job.getAStep() = checkout and // the checked-out code may lead to arbitrary code execution - checkout.getAFollowingStep() = s and + checkout.getAFollowingStep() = step and // the checkout occurs in a privileged context - inPrivilegedContext(checkout) and + inPrivilegedContext(checkout, event) and // the mutable checkout step is protected by an Insufficient access check - check.protects(checkout, j.getATriggerEvent(), "untrusted-checkout") and - not check.protects(checkout, j.getATriggerEvent(), "untrusted-checkout-toctou") -select s, checkout, s, + check.protects(checkout, event, "untrusted-checkout") and + not check.protects(checkout, event, "untrusted-checkout-toctou") +select step, checkout, step, "Insufficient protection against execution of untrusted code on a privileged workflow on check $@.", check, check.toString() diff --git a/ql/src/Security/CWE-367/UntrustedCheckoutTOCTOUHigh.ql b/ql/src/Security/CWE-367/UntrustedCheckoutTOCTOUHigh.ql index 7f584e00c9a..5956b52ccbe 100644 --- a/ql/src/Security/CWE-367/UntrustedCheckoutTOCTOUHigh.ql +++ b/ql/src/Security/CWE-367/UntrustedCheckoutTOCTOUHigh.ql @@ -16,16 +16,17 @@ import codeql.actions.security.UntrustedCheckoutQuery import codeql.actions.security.PoisonableSteps import codeql.actions.security.ControlChecks -from LocalJob j, MutableRefCheckoutStep checkout, ControlCheck check +from LocalJob job, MutableRefCheckoutStep checkout, ControlCheck check, Event event where - j.getAStep() = checkout and + job.getAStep() = checkout and // there are no evidences that the checked-out gets executed not checkout.getAFollowingStep() instanceof PoisonableStep and // the checkout occurs in a privileged context - inPrivilegedContext(checkout) and + inPrivilegedContext(checkout, event) and + event = job.getATriggerEvent() and // the mutable checkout step is protected by an Insufficient access check - check.protects(checkout, j.getATriggerEvent(), "untrusted-checkout") and - not check.protects(checkout, j.getATriggerEvent(), "untrusted-checkout-toctou") + check.protects(checkout, event, "untrusted-checkout") and + not check.protects(checkout, event, "untrusted-checkout-toctou") select checkout, "Insufficient protection against execution of untrusted code on a privileged workflow on step $@.", check, check.toString() diff --git a/ql/src/Security/CWE-829/ArtifactPoisoningCritical.ql b/ql/src/Security/CWE-829/ArtifactPoisoningCritical.ql index 82c6f936c51..e4ab90e5fc2 100644 --- a/ql/src/Security/CWE-829/ArtifactPoisoningCritical.ql +++ b/ql/src/Security/CWE-829/ArtifactPoisoningCritical.ql @@ -16,10 +16,13 @@ import codeql.actions.security.ArtifactPoisoningQuery import ArtifactPoisoningFlow::PathGraph import codeql.actions.security.ControlChecks -from ArtifactPoisoningFlow::PathNode source, ArtifactPoisoningFlow::PathNode sink +from ArtifactPoisoningFlow::PathNode source, ArtifactPoisoningFlow::PathNode sink, Event event where ArtifactPoisoningFlow::flowPath(source, sink) and - inPrivilegedContext(sink.getNode().asExpr()) + inPrivilegedContext(sink.getNode().asExpr(), event) and + not exists(ControlCheck check | + check.protects(sink.getNode().asExpr(), event, "artifact-poisoning") + ) select sink.getNode(), source, sink, "Potential artifact poisoning in $@, which may be controlled by an external user.", sink, sink.getNode().toString() diff --git a/ql/src/Security/CWE-829/ArtifactPoisoningPathTraversal.ql b/ql/src/Security/CWE-829/ArtifactPoisoningPathTraversal.ql index a50c47a9793..5f676052ef6 100644 --- a/ql/src/Security/CWE-829/ArtifactPoisoningPathTraversal.ql +++ b/ql/src/Security/CWE-829/ArtifactPoisoningPathTraversal.ql @@ -16,8 +16,9 @@ import actions import codeql.actions.security.PoisonableSteps import codeql.actions.security.UseOfKnownVulnerableActionQuery -from UsesStep download, KnownVulnerableAction vulnerable_action +from UsesStep download, KnownVulnerableAction vulnerable_action, Event event where + event = download.getEnclosingJob().getATriggerEvent() and vulnerable_action.getVulnerableAction() = download.getCallee() and download.getCallee() = "actions/download-artifact" and ( @@ -28,7 +29,7 @@ where // exists a poisonable upload artifact in the same workflow exists(UsesStep checkout, PoisonableStep poison, UsesStep upload | download.getEnclosingWorkflow().getAJob().(LocalJob).getAStep() = checkout and - download.getEnclosingJob().isPrivilegedExternallyTriggerable() and + download.getEnclosingJob().isPrivilegedExternallyTriggerable(event) and checkout.getCallee() = "actions/checkout" and checkout.getAFollowingStep() = poison and poison.getAFollowingStep() = upload and diff --git a/ql/src/Security/CWE-829/UntrustedCheckoutCritical.ql b/ql/src/Security/CWE-829/UntrustedCheckoutCritical.ql index 31a4cdf94e5..f9f95191795 100644 --- a/ql/src/Security/CWE-829/UntrustedCheckoutCritical.ql +++ b/ql/src/Security/CWE-829/UntrustedCheckoutCritical.ql @@ -25,8 +25,7 @@ where // the checkout is followed by a known poisonable step checkout.getAFollowingStep() = step and // the checkout occurs in a privileged context - inPrivilegedContext(checkout) and - event = checkout.getEnclosingJob().getATriggerEvent() and + inPrivilegedContext(step, event) and ( // issue_comment: check for date comparison checks and actor/access control checks event.getName() = "issue_comment" and @@ -36,12 +35,13 @@ where check instanceof AssociationCheck or check instanceof PermissionCheck ) and - check.dominates(checkout) and - date_check.dominates(checkout) + check.dominates(step) and + date_check.dominates(step) ) 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(step, event, "untrusted-checkout")) ) -select step, checkout, step, "Execution of untrusted code on a privileged workflow." +select step, checkout, step, "Execution of untrusted code on a privileged workflow. $@", event, + event.getLocation().getFile().toString() diff --git a/ql/src/Security/CWE-829/UntrustedCheckoutHigh.ql b/ql/src/Security/CWE-829/UntrustedCheckoutHigh.ql index bc6f0e36e56..e130ba5dbb8 100644 --- a/ql/src/Security/CWE-829/UntrustedCheckoutHigh.ql +++ b/ql/src/Security/CWE-829/UntrustedCheckoutHigh.ql @@ -23,8 +23,7 @@ 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) and - event = checkout.getEnclosingJob().getATriggerEvent() and + inPrivilegedContext(checkout, event) and ( // issue_comment: check for date comparison checks and actor/access control checks event.getName() = "issue_comment" and diff --git a/ql/src/Security/CWE-829/UntrustedCheckoutMedium.ql b/ql/src/Security/CWE-829/UntrustedCheckoutMedium.ql index 8cc8e75c2af..66c68e882e2 100644 --- a/ql/src/Security/CWE-829/UntrustedCheckoutMedium.ql +++ b/ql/src/Security/CWE-829/UntrustedCheckoutMedium.ql @@ -15,8 +15,6 @@ import actions import codeql.actions.security.UntrustedCheckoutQuery -import codeql.actions.security.PoisonableSteps -import codeql.actions.security.ControlChecks from PRHeadCheckoutStep checkout where diff --git a/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected b/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected index 609b09fdfef..3ad4e6915d2 100644 --- a/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected +++ b/ql/test/query-tests/Security/CWE-094/CodeInjectionMedium.expected @@ -438,7 +438,6 @@ subpaths | .github/workflows/composite-action-caller-3.yml:12:19:12:50 | github.event.comment.body | .github/actions/action5/action.yml:4:3:4:7 | input taint | .github/actions/action5/action.yml:9:3:14:46 | output Job outputs node [result] | .github/workflows/composite-action-caller-3.yml:9:9:13:6 | Uses Step: foo [result] | | .github/workflows/composite-action-caller-4.yml:14:19:14:56 | github.event.pull_request.title | .github/reusable_workflows/TestOrg/TestRepo/.github/actions/clone-repo/action.yaml:4:3:4:7 | input title | .github/reusable_workflows/TestOrg/TestRepo/.github/actions/clone-repo/action.yaml:14:3:16:45 | output Job outputs node [result] | .github/workflows/composite-action-caller-4.yml:10:9:17:6 | Uses Step: clone [result] | #select -| .github/actions/action1/action.yml:7:19:7:55 | github.event.pull_request.body | .github/actions/action1/action.yml:7:19:7:55 | github.event.pull_request.body | .github/actions/action1/action.yml:7:19:7:55 | github.event.pull_request.body | Potential code injection in $@, which may be controlled by an external user. | .github/actions/action1/action.yml:7:19:7:55 | github.event.pull_request.body | ${{ github.event.pull_request.body }} | | .github/actions/action3/action.yml:9:19:9:55 | github.event.pull_request.body | .github/actions/action3/action.yml:9:19:9:55 | github.event.pull_request.body | .github/actions/action3/action.yml:9:19:9:55 | github.event.pull_request.body | Potential code injection in $@, which may be controlled by an external user. | .github/actions/action3/action.yml:9:19:9:55 | github.event.pull_request.body | ${{ github.event.pull_request.body }} | | .github/actions/action4/action.yml:7:19:7:55 | github.event.pull_request.body | .github/actions/action4/action.yml:7:19:7:55 | github.event.pull_request.body | .github/actions/action4/action.yml:7:19:7:55 | github.event.pull_request.body | Potential code injection in $@, which may be controlled by an external user. | .github/actions/action4/action.yml:7:19:7:55 | github.event.pull_request.body | ${{ github.event.pull_request.body }} | | .github/workflows/changed-files.yml:20:24:20:76 | steps.changed-files1.outputs.all_changed_files | .github/workflows/changed-files.yml:15:9:18:6 | Uses Step: changed-files1 | .github/workflows/changed-files.yml:20:24:20:76 | steps.changed-files1.outputs.all_changed_files | Potential code injection in $@, which may be controlled by an external user. | .github/workflows/changed-files.yml:20:24:20:76 | steps.changed-files1.outputs.all_changed_files | ${{ steps.changed-files1.outputs.all_changed_files }} | diff --git a/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected b/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected index afae2454078..006f365ae05 100644 --- a/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected +++ b/ql/test/query-tests/Security/CWE-829/UntrustedCheckoutCritical.expected @@ -246,37 +246,42 @@ edges | .github/workflows/untrusted_checkout.yml:16:9:20:6 | Uses Step | .github/workflows/untrusted_checkout.yml:20:9:22:23 | Run Step | | .github/workflows/workflow_run_untrusted_checkout.yml:13:9:16:6 | Uses Step | .github/workflows/workflow_run_untrusted_checkout.yml:16:9:18:31 | Uses Step | #select -| .github/reusable_workflows/TestOrg/TestRepo/.github/workflows/reusable.yml:26:9:29:7 | Run Step | .github/reusable_workflows/TestOrg/TestRepo/.github/workflows/reusable.yml:23:9:26:6 | Uses Step | .github/reusable_workflows/TestOrg/TestRepo/.github/workflows/reusable.yml:26:9:29:7 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/auto_ci.yml:32:9:37:6 | Run Step | .github/workflows/auto_ci.yml:20:9:27:6 | Uses Step | .github/workflows/auto_ci.yml:32:9:37:6 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/auto_ci.yml:48:9:52:2 | Run Step | .github/workflows/auto_ci.yml:20:9:27:6 | Uses Step | .github/workflows/auto_ci.yml:48:9:52:2 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/auto_ci.yml:79:9:84:6 | Run Step | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:79:9:84:6 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/auto_ci.yml:84:9:93:6 | Run Step | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:84:9:93:6 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/dependabot3.yml:25:9:48:6 | Run Step: set-milestone | .github/workflows/dependabot3.yml:15:9:20:6 | Uses Step | .github/workflows/dependabot3.yml:25:9:48:6 | Run Step: set-milestone | Execution of untrusted code on a privileged workflow. | -| .github/workflows/gitcheckout.yml:21:11:23:22 | Run Step | .github/workflows/gitcheckout.yml:10:11:18:8 | Run Step | .github/workflows/gitcheckout.yml:21:11:23:22 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/level0.yml:107:9:112:2 | Run Step | .github/workflows/level0.yml:99:9:103:6 | Uses Step | .github/workflows/level0.yml:107:9:112:2 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/level0.yml:133:9:135:23 | Run Step | .github/workflows/level0.yml:125:9:129:6 | Uses Step | .github/workflows/level0.yml:133:9:135:23 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/poc2.yml:42:9:47:6 | Uses Step | .github/workflows/poc2.yml:37:9:42:6 | Uses Step | .github/workflows/poc2.yml:42:9:47:6 | Uses Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/poc2.yml:52:9:58:24 | Run Step | .github/workflows/poc2.yml:37:9:42:6 | Uses Step | .github/workflows/poc2.yml:52:9:58:24 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/pr-workflow.yml:222:9:227:6 | Uses Step | .github/workflows/pr-workflow.yml:216:9:222:6 | Uses Step | .github/workflows/pr-workflow.yml:222:9:227:6 | Uses Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/pr-workflow.yml:256:9:261:6 | Uses Step | .github/workflows/pr-workflow.yml:250:9:256:6 | Uses Step | .github/workflows/pr-workflow.yml:256:9:261:6 | Uses Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/pr-workflow.yml:290:9:295:6 | Uses Step | .github/workflows/pr-workflow.yml:284:9:290:6 | Uses Step | .github/workflows/pr-workflow.yml:290:9:295:6 | Uses Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/pr-workflow.yml:391:9:395:6 | Uses Step | .github/workflows/pr-workflow.yml:386:9:391:6 | Uses Step | .github/workflows/pr-workflow.yml:391:9:395:6 | Uses Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/pr-workflow.yml:395:9:404:6 | Uses Step | .github/workflows/pr-workflow.yml:386:9:391:6 | Uses Step | .github/workflows/pr-workflow.yml:395:9:404:6 | Uses Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/pr-workflow.yml:404:9:414:6 | Uses Step | .github/workflows/pr-workflow.yml:386:9:391:6 | Uses Step | .github/workflows/pr-workflow.yml:404:9:414:6 | Uses Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/pr-workflow.yml:414:9:423:6 | Uses Step | .github/workflows/pr-workflow.yml:386:9:391:6 | Uses Step | .github/workflows/pr-workflow.yml:414:9:423:6 | Uses Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/pr-workflow.yml:423:9:432:2 | Uses Step | .github/workflows/pr-workflow.yml:386:9:391:6 | Uses Step | .github/workflows/pr-workflow.yml:423:9:432:2 | Uses Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/reusable_local.yml:26:9:29:7 | Run Step | .github/workflows/reusable_local.yml:23:9:26:6 | Uses Step | .github/workflows/reusable_local.yml:26:9:29:7 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/test5.yml:32:9:34:2 | Run Step | .github/workflows/test5.yml:28:9:32:6 | Uses Step | .github/workflows/test5.yml:32:9:34:2 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/test5.yml:58:9:60:2 | Run Step | .github/workflows/test5.yml:54:9:58:6 | Uses Step | .github/workflows/test5.yml:58:9:60:2 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/test5.yml:68:9:68:43 | Run Step | .github/workflows/test5.yml:64:9:68:6 | Uses Step | .github/workflows/test5.yml:68:9:68:43 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/test7.yml:33:9:36:6 | Run Step | .github/workflows/test7.yml:19:9:24:6 | Uses Step | .github/workflows/test7.yml:33:9:36:6 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/test7.yml:36:9:39:6 | Run Step | .github/workflows/test7.yml:19:9:24:6 | Uses Step | .github/workflows/test7.yml:36:9:39:6 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/test7.yml:49:9:58:20 | Run Step: benchmark-pr | .github/workflows/test7.yml:19:9:24:6 | Uses Step | .github/workflows/test7.yml:49:9:58:20 | Run Step: benchmark-pr | Execution of untrusted code on a privileged workflow. | -| .github/workflows/test10.yml:25:9:30:2 | Run Step | .github/workflows/test10.yml:20:9:25:6 | Uses Step | .github/workflows/test10.yml:25:9:30:2 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/test11.yml:90:7:93:54 | Uses Step | .github/workflows/test11.yml:84:7:90:4 | Uses Step | .github/workflows/test11.yml:90:7:93:54 | Uses Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/untrusted_checkout3.yml:13:9:13:23 | Run Step | .github/actions/dangerous-git-checkout/action.yml:6:7:11:4 | Uses Step | .github/workflows/untrusted_checkout3.yml:13:9:13:23 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/untrusted_checkout4.yml:61:7:67:4 | Run Step | .github/workflows/untrusted_checkout4.yml:55:7:61:4 | Uses Step | .github/workflows/untrusted_checkout4.yml:61:7:67:4 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/untrusted_checkout4.yml:67:7:73:4 | Run Step | .github/workflows/untrusted_checkout4.yml:55:7:61:4 | Uses Step | .github/workflows/untrusted_checkout4.yml:67:7:73:4 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/untrusted_checkout4.yml:73:7:79:4 | Run Step | .github/workflows/untrusted_checkout4.yml:55:7:61:4 | Uses Step | .github/workflows/untrusted_checkout4.yml:73:7:79:4 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/untrusted_checkout.yml:20:9:22:23 | Run Step | .github/workflows/untrusted_checkout.yml:10:9:13:6 | Uses Step | .github/workflows/untrusted_checkout.yml:20:9:22:23 | Run Step | Execution of untrusted code on a privileged workflow. | -| .github/workflows/untrusted_checkout.yml:20:9:22:23 | Run Step | .github/workflows/untrusted_checkout.yml:13:9:16:6 | Uses Step | .github/workflows/untrusted_checkout.yml:20:9:22:23 | Run Step | Execution of untrusted code on a privileged workflow. | +| .github/reusable_workflows/TestOrg/TestRepo/.github/workflows/reusable.yml:26:9:29:7 | Run Step | .github/reusable_workflows/TestOrg/TestRepo/.github/workflows/reusable.yml:23:9:26:6 | Uses Step | .github/reusable_workflows/TestOrg/TestRepo/.github/workflows/reusable.yml:26:9:29:7 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/reusable_caller1.yaml:4:3:4:21 | pull_request_target | .github/workflows/reusable_caller1.yaml | +| .github/workflows/actor_trusted_checkout.yml:15:7:19:4 | Run Step | .github/workflows/actor_trusted_checkout.yml:9:7:14:4 | Uses Step | .github/workflows/actor_trusted_checkout.yml:15:7:19:4 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/actor_trusted_checkout.yml:2:3:2:21 | pull_request_target | .github/workflows/actor_trusted_checkout.yml | +| .github/workflows/auto_ci.yml:32:9:37:6 | Run Step | .github/workflows/auto_ci.yml:20:9:27:6 | Uses Step | .github/workflows/auto_ci.yml:32:9:37:6 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/auto_ci.yml:6:3:6:21 | pull_request_target | .github/workflows/auto_ci.yml | +| .github/workflows/auto_ci.yml:48:9:52:2 | Run Step | .github/workflows/auto_ci.yml:20:9:27:6 | Uses Step | .github/workflows/auto_ci.yml:48:9:52:2 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/auto_ci.yml:6:3:6:21 | pull_request_target | .github/workflows/auto_ci.yml | +| .github/workflows/auto_ci.yml:79:9:84:6 | Run Step | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:79:9:84:6 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/auto_ci.yml:6:3:6:21 | pull_request_target | .github/workflows/auto_ci.yml | +| .github/workflows/auto_ci.yml:84:9:93:6 | Run Step | .github/workflows/auto_ci.yml:67:9:74:6 | Uses Step | .github/workflows/auto_ci.yml:84:9:93:6 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/auto_ci.yml:6:3:6:21 | pull_request_target | .github/workflows/auto_ci.yml | +| .github/workflows/dependabot3.yml:25:9:48:6 | Run Step: set-milestone | .github/workflows/dependabot3.yml:15:9:20:6 | Uses Step | .github/workflows/dependabot3.yml:25:9:48:6 | Run Step: set-milestone | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/dependabot3.yml:3:5:3:23 | pull_request_target | .github/workflows/dependabot3.yml | +| .github/workflows/gitcheckout.yml:21:11:23:22 | Run Step | .github/workflows/gitcheckout.yml:10:11:18:8 | Run Step | .github/workflows/gitcheckout.yml:21:11:23:22 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/gitcheckout.yml:2:3:2:21 | pull_request_target | .github/workflows/gitcheckout.yml | +| .github/workflows/level0.yml:107:9:112:2 | Run Step | .github/workflows/level0.yml:99:9:103:6 | Uses Step | .github/workflows/level0.yml:107:9:112:2 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/level0.yml:3:3:3:8 | issues | .github/workflows/level0.yml | +| .github/workflows/level0.yml:107:9:112:2 | Run Step | .github/workflows/level0.yml:99:9:103:6 | Uses Step | .github/workflows/level0.yml:107:9:112:2 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/level0.yml:5:3:5:15 | issue_comment | .github/workflows/level0.yml | +| .github/workflows/level0.yml:107:9:112:2 | Run Step | .github/workflows/level0.yml:99:9:103:6 | Uses Step | .github/workflows/level0.yml:107:9:112:2 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/level0.yml:7:3:7:21 | pull_request_target | .github/workflows/level0.yml | +| .github/workflows/level0.yml:133:9:135:23 | Run Step | .github/workflows/level0.yml:125:9:129:6 | Uses Step | .github/workflows/level0.yml:133:9:135:23 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/level0.yml:3:3:3:8 | issues | .github/workflows/level0.yml | +| .github/workflows/level0.yml:133:9:135:23 | Run Step | .github/workflows/level0.yml:125:9:129:6 | Uses Step | .github/workflows/level0.yml:133:9:135:23 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/level0.yml:5:3:5:15 | issue_comment | .github/workflows/level0.yml | +| .github/workflows/level0.yml:133:9:135:23 | Run Step | .github/workflows/level0.yml:125:9:129:6 | Uses Step | .github/workflows/level0.yml:133:9:135:23 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/level0.yml:7:3:7:21 | pull_request_target | .github/workflows/level0.yml | +| .github/workflows/poc2.yml:42:9:47:6 | Uses Step | .github/workflows/poc2.yml:37:9:42:6 | Uses Step | .github/workflows/poc2.yml:42:9:47:6 | Uses Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/poc2.yml:4:3:4:15 | issue_comment | .github/workflows/poc2.yml | +| .github/workflows/poc2.yml:52:9:58:24 | Run Step | .github/workflows/poc2.yml:37:9:42:6 | Uses Step | .github/workflows/poc2.yml:52:9:58:24 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/poc2.yml:4:3:4:15 | issue_comment | .github/workflows/poc2.yml | +| .github/workflows/pr-workflow.yml:222:9:227:6 | Uses Step | .github/workflows/pr-workflow.yml:216:9:222:6 | Uses Step | .github/workflows/pr-workflow.yml:222:9:227:6 | Uses Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/pr-workflow-fork.yaml:7:3:7:21 | pull_request_target | .github/workflows/pr-workflow-fork.yaml | +| .github/workflows/pr-workflow.yml:256:9:261:6 | Uses Step | .github/workflows/pr-workflow.yml:250:9:256:6 | Uses Step | .github/workflows/pr-workflow.yml:256:9:261:6 | Uses Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/pr-workflow-fork.yaml:7:3:7:21 | pull_request_target | .github/workflows/pr-workflow-fork.yaml | +| .github/workflows/pr-workflow.yml:290:9:295:6 | Uses Step | .github/workflows/pr-workflow.yml:284:9:290:6 | Uses Step | .github/workflows/pr-workflow.yml:290:9:295:6 | Uses Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/pr-workflow-fork.yaml:7:3:7:21 | pull_request_target | .github/workflows/pr-workflow-fork.yaml | +| .github/workflows/pr-workflow.yml:391:9:395:6 | Uses Step | .github/workflows/pr-workflow.yml:386:9:391:6 | Uses Step | .github/workflows/pr-workflow.yml:391:9:395:6 | Uses Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/pr-workflow-fork.yaml:7:3:7:21 | pull_request_target | .github/workflows/pr-workflow-fork.yaml | +| .github/workflows/pr-workflow.yml:395:9:404:6 | Uses Step | .github/workflows/pr-workflow.yml:386:9:391:6 | Uses Step | .github/workflows/pr-workflow.yml:395:9:404:6 | Uses Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/pr-workflow-fork.yaml:7:3:7:21 | pull_request_target | .github/workflows/pr-workflow-fork.yaml | +| .github/workflows/pr-workflow.yml:404:9:414:6 | Uses Step | .github/workflows/pr-workflow.yml:386:9:391:6 | Uses Step | .github/workflows/pr-workflow.yml:404:9:414:6 | Uses Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/pr-workflow-fork.yaml:7:3:7:21 | pull_request_target | .github/workflows/pr-workflow-fork.yaml | +| .github/workflows/pr-workflow.yml:414:9:423:6 | Uses Step | .github/workflows/pr-workflow.yml:386:9:391:6 | Uses Step | .github/workflows/pr-workflow.yml:414:9:423:6 | Uses Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/pr-workflow-fork.yaml:7:3:7:21 | pull_request_target | .github/workflows/pr-workflow-fork.yaml | +| .github/workflows/pr-workflow.yml:423:9:432:2 | Uses Step | .github/workflows/pr-workflow.yml:386:9:391:6 | Uses Step | .github/workflows/pr-workflow.yml:423:9:432:2 | Uses Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/pr-workflow-fork.yaml:7:3:7:21 | pull_request_target | .github/workflows/pr-workflow-fork.yaml | +| .github/workflows/reusable_local.yml:26:9:29:7 | Run Step | .github/workflows/reusable_local.yml:23:9:26:6 | Uses Step | .github/workflows/reusable_local.yml:26:9:29:7 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/reusable_caller3.yaml:4:3:4:21 | pull_request_target | .github/workflows/reusable_caller3.yaml | +| .github/workflows/test5.yml:32:9:34:2 | Run Step | .github/workflows/test5.yml:28:9:32:6 | Uses Step | .github/workflows/test5.yml:32:9:34:2 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/test5.yml:4:3:4:15 | issue_comment | .github/workflows/test5.yml | +| .github/workflows/test5.yml:58:9:60:2 | Run Step | .github/workflows/test5.yml:54:9:58:6 | Uses Step | .github/workflows/test5.yml:58:9:60:2 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/test5.yml:4:3:4:15 | issue_comment | .github/workflows/test5.yml | +| .github/workflows/test5.yml:68:9:68:43 | Run Step | .github/workflows/test5.yml:64:9:68:6 | Uses Step | .github/workflows/test5.yml:68:9:68:43 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/test5.yml:4:3:4:15 | issue_comment | .github/workflows/test5.yml | +| .github/workflows/test7.yml:33:9:36:6 | Run Step | .github/workflows/test7.yml:19:9:24:6 | Uses Step | .github/workflows/test7.yml:33:9:36:6 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/test7.yml:4:3:4:15 | issue_comment | .github/workflows/test7.yml | +| .github/workflows/test7.yml:36:9:39:6 | Run Step | .github/workflows/test7.yml:19:9:24:6 | Uses Step | .github/workflows/test7.yml:36:9:39:6 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/test7.yml:4:3:4:15 | issue_comment | .github/workflows/test7.yml | +| .github/workflows/test7.yml:49:9:58:20 | Run Step: benchmark-pr | .github/workflows/test7.yml:19:9:24:6 | Uses Step | .github/workflows/test7.yml:49:9:58:20 | Run Step: benchmark-pr | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/test7.yml:4:3:4:15 | issue_comment | .github/workflows/test7.yml | +| .github/workflows/test10.yml:25:9:30:2 | Run Step | .github/workflows/test10.yml:20:9:25:6 | Uses Step | .github/workflows/test10.yml:25:9:30:2 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/test10.yml:8:3:8:21 | pull_request_target | .github/workflows/test10.yml | +| .github/workflows/test11.yml:90:7:93:54 | Uses Step | .github/workflows/test11.yml:84:7:90:4 | Uses Step | .github/workflows/test11.yml:90:7:93:54 | Uses Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/test11.yml:5:3:5:15 | issue_comment | .github/workflows/test11.yml | +| .github/workflows/untrusted_checkout3.yml:13:9:13:23 | Run Step | .github/actions/dangerous-git-checkout/action.yml:6:7:11:4 | Uses Step | .github/workflows/untrusted_checkout3.yml:13:9:13:23 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/untrusted_checkout3.yml:4:3:4:14 | workflow_run | .github/workflows/untrusted_checkout3.yml | +| .github/workflows/untrusted_checkout4.yml:61:7:67:4 | Run Step | .github/workflows/untrusted_checkout4.yml:55:7:61:4 | Uses Step | .github/workflows/untrusted_checkout4.yml:61:7:67:4 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/untrusted_checkout4.yml:4:3:4:15 | issue_comment | .github/workflows/untrusted_checkout4.yml | +| .github/workflows/untrusted_checkout4.yml:67:7:73:4 | Run Step | .github/workflows/untrusted_checkout4.yml:55:7:61:4 | Uses Step | .github/workflows/untrusted_checkout4.yml:67:7:73:4 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/untrusted_checkout4.yml:4:3:4:15 | issue_comment | .github/workflows/untrusted_checkout4.yml | +| .github/workflows/untrusted_checkout4.yml:73:7:79:4 | Run Step | .github/workflows/untrusted_checkout4.yml:55:7:61:4 | Uses Step | .github/workflows/untrusted_checkout4.yml:73:7:79:4 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/untrusted_checkout4.yml:4:3:4:15 | issue_comment | .github/workflows/untrusted_checkout4.yml | +| .github/workflows/untrusted_checkout.yml:20:9:22:23 | Run Step | .github/workflows/untrusted_checkout.yml:10:9:13:6 | Uses Step | .github/workflows/untrusted_checkout.yml:20:9:22:23 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/untrusted_checkout.yml:2:3:2:21 | pull_request_target | .github/workflows/untrusted_checkout.yml | +| .github/workflows/untrusted_checkout.yml:20:9:22:23 | Run Step | .github/workflows/untrusted_checkout.yml:13:9:16:6 | Uses Step | .github/workflows/untrusted_checkout.yml:20:9:22:23 | Run Step | Execution of untrusted code on a privileged workflow. $@ | .github/workflows/untrusted_checkout.yml:2:3:2:21 | pull_request_target | .github/workflows/untrusted_checkout.yml |