diff --git a/ql/lib/codeql/actions/Ast.qll b/ql/lib/codeql/actions/Ast.qll index a1651eedc47..17b0dab4ee6 100644 --- a/ql/lib/codeql/actions/Ast.qll +++ b/ql/lib/codeql/actions/Ast.qll @@ -289,6 +289,8 @@ class Run extends Step instanceof RunImpl { ScalarValue getScriptScalar() { result = super.getScriptScalar() } Expression getAnScriptExpr() { result = super.getAnScriptExpr() } + + string getWorkingDirectory() { result = super.getWorkingDirectory() } } abstract class SimpleReferenceExpression extends AstNode instanceof SimpleReferenceExpressionImpl { diff --git a/ql/lib/codeql/actions/Helper.qll b/ql/lib/codeql/actions/Helper.qll index 9356950f571..f9fa108ec3a 100644 --- a/ql/lib/codeql/actions/Helper.qll +++ b/ql/lib/codeql/actions/Helper.qll @@ -283,3 +283,30 @@ string getRepoRoot() { result = "" ) } + +bindingset[path] +string normalizePath(string path) { + exists(string trimmed_path | trimmed_path = trimQuotes(path) | + // ./foo -> GITHUB_WORKSPACE/foo + if path.indexOf("./") = 0 + then result = path.replaceAll("./", "GITHUB_WORKSPACE/") + else + // GITHUB_WORKSPACE/foo -> GITHUB_WORKSPACE/foo + if path.indexOf("GITHUB_WORKSPACE/") = 0 + then result = path + else + // foo -> GITHUB_WORKSPACE/foo + if path.regexpMatch("^[^/~].*") + then result = "GITHUB_WORKSPACE/" + path.regexpReplaceAll("/$", "") + else + // ~/foo -> ~/foo + // /foo -> /foo + result = path + ) +} + +/** + * Holds if the path cache_path is a subpath of the path untrusted_path. + */ +bindingset[subpath, path] +predicate isSubpath(string subpath, string path) { subpath.substring(0, path.length()) = path } diff --git a/ql/lib/codeql/actions/ast/internal/Ast.qll b/ql/lib/codeql/actions/ast/internal/Ast.qll index 154d466ab7d..5361943331b 100644 --- a/ql/lib/codeql/actions/ast/internal/Ast.qll +++ b/ql/lib/codeql/actions/ast/internal/Ast.qll @@ -1317,6 +1317,18 @@ class RunImpl extends StepImpl { override string toString() { if exists(this.getId()) then result = "Run Step: " + this.getId() else result = "Run Step" } + + /** Gets the working directory for this `runs` mapping. */ + string getWorkingDirectory() { + if exists(n.lookup("working-directory").(YamlString).getValue()) + then + result = + n.lookup("working-directory") + .(YamlString) + .getValue() + .regexpReplaceAll("^\\./", "GITHUB_WORKSPACE/") + else result = "GITHUB_WORKSPACE/" + } } /** diff --git a/ql/lib/codeql/actions/security/ArtifactPoisoningQuery.qll b/ql/lib/codeql/actions/security/ArtifactPoisoningQuery.qll index 236cc4d8091..ebe22140be2 100644 --- a/ql/lib/codeql/actions/security/ArtifactPoisoningQuery.qll +++ b/ql/lib/codeql/actions/security/ArtifactPoisoningQuery.qll @@ -35,7 +35,9 @@ class GitHubDownloadArtifactActionStep extends UntrustedArtifactDownloadStep, Us } override string getPath() { - if exists(this.getArgument("path")) then result = this.getArgument("path") else result = "" + if exists(this.getArgument("path")) + then result = normalizePath(this.getArgument("path")) + else result = "GITHUB_WORKSPACE/" } } @@ -79,11 +81,11 @@ class DownloadArtifactActionStep extends UntrustedArtifactDownloadStep, UsesStep override string getPath() { if exists(this.getArgument(["path", "download_path"])) - then result = this.getArgument(["path", "download_path"]) + then result = normalizePath(this.getArgument(["path", "download_path"])) else if exists(this.getArgument("paths")) - then result = this.getArgument("paths").splitAt(" ") - else result = "" + then result = normalizePath(this.getArgument("paths").splitAt(" ")) + else result = "GITHUB_WORKSPACE/" } } @@ -114,8 +116,8 @@ class LegitLabsDownloadArtifactActionStep extends UntrustedArtifactDownloadStep, override string getPath() { if exists(this.getArgument("path")) - then result = this.getArgument("path") - else result = "./artifacts" + then result = normalizePath(this.getArgument("path")) + else result = "GITHUB_WORKSPACE/artifacts" } } @@ -161,14 +163,14 @@ class ActionsGitHubScriptDownloadStep extends UntrustedArtifactDownloadStep, Use .regexpMatch(unzipRegexp() + unzipDirArgRegexp()) then result = - trimQuotes(this.getAFollowingStep() - .(Run) - .getScript() - .splitAt("\n") - .regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2)) + normalizePath(trimQuotes(this.getAFollowingStep() + .(Run) + .getScript() + .splitAt("\n") + .regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2))) else if this.getAFollowingStep().(Run).getScript().splitAt("\n").regexpMatch(unzipRegexp()) - then result = "" + then result = "GITHUB_WORKSPACE/" else none() } } @@ -197,18 +199,20 @@ class GHRunArtifactDownloadStep extends UntrustedArtifactDownloadStep, Run { script.splitAt("\n").regexpMatch(unzipRegexp() + unzipDirArgRegexp()) then result = - trimQuotes(script.splitAt("\n").regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2)) or + normalizePath(trimQuotes(script + .splitAt("\n") + .regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2))) or result = - trimQuotes(this.getAFollowingStep() - .(Run) - .getScript() - .splitAt("\n") - .regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2)) + normalizePath(trimQuotes(this.getAFollowingStep() + .(Run) + .getScript() + .splitAt("\n") + .regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2))) else if this.getAFollowingStep().(Run).getScript().splitAt("\n").regexpMatch(unzipRegexp()) or script.splitAt("\n").regexpMatch(unzipRegexp()) - then result = "" + then result = "GITHUB_WORKSPACE/" else none() } } @@ -244,14 +248,16 @@ class DirectArtifactDownloadStep extends UntrustedArtifactDownloadStep, Run { .regexpMatch(unzipRegexp() + unzipDirArgRegexp()) then result = - trimQuotes(script.splitAt("\n").regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2)) or + normalizePath(trimQuotes(script + .splitAt("\n") + .regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2))) or result = - trimQuotes(this.getAFollowingStep() - .(Run) - .getScript() - .splitAt("\n") - .regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2)) - else result = "" + normalizePath(trimQuotes(this.getAFollowingStep() + .(Run) + .getScript() + .splitAt("\n") + .regexpCapture(unzipRegexp() + unzipDirArgRegexp(), 2))) + else result = "GITHUB_WORKSPACE/" } } @@ -268,18 +274,16 @@ class ArtifactPoisoningSink extends DataFlow::Node { ( // 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(), download.getPath()) + or // Checking the path for non local script execution steps is very difficult not poisonable instanceof LocalScriptExecutionRunStep - or - // TODO: account for Run's working directory - poisonable - .(LocalScriptExecutionRunStep) - .getCommand() - .matches(["./", ""] + download.getPath() + "%") + // 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(), download.getPath()) ) or poisonable.(UsesStep) = this.asExpr() and - download.getPath() = "" + download.getPath() = "GITHUB_WORKSPACE/" ) } diff --git a/ql/lib/codeql/actions/security/CachePoisoningQuery.qll b/ql/lib/codeql/actions/security/CachePoisoningQuery.qll index 56002cb2b16..a0113beed46 100644 --- a/ql/lib/codeql/actions/security/CachePoisoningQuery.qll +++ b/ql/lib/codeql/actions/security/CachePoisoningQuery.qll @@ -51,13 +51,17 @@ abstract class CacheWritingStep extends Step { class CacheActionUsesStep extends CacheWritingStep, UsesStep { CacheActionUsesStep() { this.getCallee() = "actions/cache" } - override string getPath() { result = this.(UsesStep).getArgument("path").splitAt("\n") } + override string getPath() { + result = normalizePath(this.(UsesStep).getArgument("path").splitAt("\n")) + } } class CacheActionSaveUsesStep extends CacheWritingStep, UsesStep { CacheActionSaveUsesStep() { this.getCallee() = "actions/cache/save" } - override string getPath() { result = this.(UsesStep).getArgument("path").splitAt("\n") } + override string getPath() { + result = normalizePath(this.(UsesStep).getArgument("path").splitAt("\n")) + } } class SetupRubyUsesStep extends CacheWritingStep, UsesStep { @@ -66,5 +70,5 @@ class SetupRubyUsesStep extends CacheWritingStep, UsesStep { this.getArgument("bundler-cache") = "true" } - override string getPath() { result = "vendor/bundle" } + override string getPath() { result = normalizePath("vendor/bundle") } } diff --git a/ql/lib/codeql/actions/security/PoisonableSteps.qll b/ql/lib/codeql/actions/security/PoisonableSteps.qll index 5dd0081f61e..67bbfa2a4fe 100644 --- a/ql/lib/codeql/actions/security/PoisonableSteps.qll +++ b/ql/lib/codeql/actions/security/PoisonableSteps.qll @@ -36,18 +36,18 @@ class JavascriptImportnUsesStep extends PoisonableStep, UsesStep { } class LocalScriptExecutionRunStep extends PoisonableStep, Run { - string cmd; + string path; LocalScriptExecutionRunStep() { - exists(string line, string regexp, int command_group | + exists(string line, string regexp, int path_group | line = this.getScript().splitAt("\n").trim() | - poisonableLocalScriptsDataModel(regexp, command_group) and - cmd = line.regexpCapture(regexp, command_group) + poisonableLocalScriptsDataModel(regexp, path_group) and + path = line.regexpCapture(regexp, path_group) ) } - string getCommand() { result = cmd } + string getPath() { result = normalizePath(path.splitAt(" ")) } } class LocalActionUsesStep extends PoisonableStep, UsesStep { diff --git a/ql/lib/codeql/actions/security/UntrustedCheckoutQuery.qll b/ql/lib/codeql/actions/security/UntrustedCheckoutQuery.qll index df3e1e4d8a2..100a9c5dd5d 100644 --- a/ql/lib/codeql/actions/security/UntrustedCheckoutQuery.qll +++ b/ql/lib/codeql/actions/security/UntrustedCheckoutQuery.qll @@ -151,12 +151,6 @@ predicate containsHeadRef(string s) { ) } -private string getStepCWD() { - // TODO: This should be the path of the git command. - // Read if from the step's CWD, workspace or look for a cd command. - result = "?" -} - /** Checkout of a Pull Request HEAD */ abstract class PRHeadCheckoutStep extends Step { abstract string getPath(); @@ -208,7 +202,7 @@ class ActionsMutableRefCheckout extends MutableRefCheckoutStep instanceof UsesSt override string getPath() { if exists(this.(UsesStep).getArgument("path")) then result = this.(UsesStep).getArgument("path") - else result = "?" + else result = "GITHUB_WORKSPACE/" } } @@ -252,7 +246,7 @@ class ActionsSHACheckout extends SHACheckoutStep instanceof UsesStep { override string getPath() { if exists(this.(UsesStep).getArgument("path")) then result = this.(UsesStep).getArgument("path") - else result = "?" + else result = "GITHUB_WORKSPACE/" } } @@ -277,7 +271,7 @@ class GitMutableRefCheckout extends MutableRefCheckoutStep instanceof Run { ) } - override string getPath() { result = getStepCWD() } + override string getPath() { result = this.(Run).getWorkingDirectory() } } /** Checkout of a Pull Request HEAD ref using git within a Run step */ @@ -298,7 +292,7 @@ class GitSHACheckout extends SHACheckoutStep instanceof Run { ) } - override string getPath() { result = getStepCWD() } + override string getPath() { result = this.(Run).getWorkingDirectory() } } /** Checkout of a Pull Request HEAD ref using gh within a Run step */ @@ -321,7 +315,7 @@ class GhMutableRefCheckout extends MutableRefCheckoutStep instanceof Run { ) } - override string getPath() { result = getStepCWD() } + override string getPath() { result = this.(Run).getWorkingDirectory() } } /** Checkout of a Pull Request HEAD ref using gh within a Run step */ @@ -341,5 +335,5 @@ class GhSHACheckout extends SHACheckoutStep instanceof Run { ) } - override string getPath() { result = getStepCWD() } + override string getPath() { result = this.(Run).getWorkingDirectory() } } diff --git a/ql/src/Security/CWE-349/CachePoisoningViaDirectCache.ql b/ql/src/Security/CWE-349/CachePoisoningViaDirectCache.ql index bda8224925e..91bb4d3bc5a 100644 --- a/ql/src/Security/CWE-349/CachePoisoningViaDirectCache.ql +++ b/ql/src/Security/CWE-349/CachePoisoningViaDirectCache.ql @@ -18,29 +18,6 @@ import codeql.actions.security.CachePoisoningQuery import codeql.actions.security.PoisonableSteps import codeql.actions.security.ControlChecks -/** - * Holds if the path cache_path is a subpath of the path untrusted_path. - */ -bindingset[cache_path, untrusted_path] -predicate controlledCachePath(string cache_path, string untrusted_path) { - exists(string normalized_cache_path, string normalized_untrusted_path | - ( - cache_path.regexpMatch("^[a-zA-Z0-9_-].*") and - normalized_cache_path = "./" + cache_path.regexpReplaceAll("/$", "") - or - normalized_cache_path = cache_path.regexpReplaceAll("/$", "") - ) and - ( - untrusted_path.regexpMatch("^[a-zA-Z0-9_-].*") and - normalized_untrusted_path = "./" + untrusted_path.regexpReplaceAll("/$", "") - or - normalized_untrusted_path = untrusted_path.regexpReplaceAll("/$", "") - ) and - normalized_cache_path.substring(0, normalized_untrusted_path.length()) = - normalized_untrusted_path - ) -} - query predicate edges(Step a, Step b) { a.getNextStep() = b } from LocalJob job, Event event, Step source, Step step, string message, string path @@ -86,7 +63,7 @@ where step.(CacheWritingStep).getPath() = "?" or // the cache writing step reads from a path the attacker can control - not path = "?" and controlledCachePath(step.(CacheWritingStep).getPath(), path) + not path = "?" and isSubpath(step.(CacheWritingStep).getPath(), path) ) and not step instanceof PoisonableStep select step, source, step, diff --git a/ql/test/query-tests/Security/CWE-349/.github/workflows/direct_cache6.yml b/ql/test/query-tests/Security/CWE-349/.github/workflows/direct_cache6.yml index 5948474d21a..b9652d46b59 100644 --- a/ql/test/query-tests/Security/CWE-349/.github/workflows/direct_cache6.yml +++ b/ql/test/query-tests/Security/CWE-349/.github/workflows/direct_cache6.yml @@ -1,7 +1,7 @@ name: Test on: - issue_comment: + pull_request_target: permissions: actions: write @@ -11,6 +11,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} - name: Set up Python 3.10 uses: actions/setup-python@v5 with: @@ -22,14 +24,3 @@ jobs: path: ./results/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} restore-keys: ${{ runner.os }}-pip- - - name: Download artifact - uses: dawidd6/action-download-artifact@v2 - with: - name: results - path: results/ - - name: Upload results - uses: actions/upload-artifact@v4 - with: - name: results - path: results/ - if-no-files-found: ignore diff --git a/ql/test/query-tests/Security/CWE-349/.github/workflows/neg_direct_cache4.yml b/ql/test/query-tests/Security/CWE-349/.github/workflows/neg_direct_cache4.yml new file mode 100644 index 00000000000..9afe62d69da --- /dev/null +++ b/ql/test/query-tests/Security/CWE-349/.github/workflows/neg_direct_cache4.yml @@ -0,0 +1,23 @@ +on: + issue_comment: + types: [created] + +jobs: + pr-comment: + permissions: read-all + runs-on: ubuntu-latest + steps: + - uses: xt0rted/pull-request-comment-branch@v2 + id: comment-branch + + - uses: actions/checkout@v3 + if: success() + with: + ref: ${{ steps.comment-branch.outputs.head_sha }} + + - uses: actions/cache@v2 + with: + path: ~/.grade/caches/ + key: poison_key + - run: | + cat poison diff --git a/ql/test/query-tests/Security/CWE-349/.github/workflows/neg_direct_cache5.yml b/ql/test/query-tests/Security/CWE-349/.github/workflows/neg_direct_cache5.yml new file mode 100644 index 00000000000..b39bc7a880f --- /dev/null +++ b/ql/test/query-tests/Security/CWE-349/.github/workflows/neg_direct_cache5.yml @@ -0,0 +1,23 @@ +on: + issue_comment: + types: [created] + +jobs: + pr-comment: + permissions: read-all + runs-on: ubuntu-latest + steps: + - uses: xt0rted/pull-request-comment-branch@v2 + id: comment-branch + + - uses: actions/checkout@v3 + if: success() + with: + ref: ${{ steps.comment-branch.outputs.head_sha }} + + - uses: actions/cache@v2 + with: + path: /tmp/caches/ + key: poison_key + - run: | + cat poison diff --git a/ql/test/query-tests/Security/CWE-349/CachePoisoningViaDirectCache.expected b/ql/test/query-tests/Security/CWE-349/CachePoisoningViaDirectCache.expected index 8bd69d8f245..f45755adf1d 100644 --- a/ql/test/query-tests/Security/CWE-349/CachePoisoningViaDirectCache.expected +++ b/ql/test/query-tests/Security/CWE-349/CachePoisoningViaDirectCache.expected @@ -12,10 +12,8 @@ edges | .github/workflows/direct_cache4.yml:17:9:21:6 | Uses Step | .github/workflows/direct_cache4.yml:21:9:22:21 | Run Step | | .github/workflows/direct_cache5.yml:14:9:17:6 | Uses Step | .github/workflows/direct_cache5.yml:17:9:21:6 | Uses Step | | .github/workflows/direct_cache5.yml:17:9:21:6 | Uses Step | .github/workflows/direct_cache5.yml:21:9:22:21 | Run Step | -| .github/workflows/direct_cache6.yml:13:9:14:6 | Uses Step | .github/workflows/direct_cache6.yml:14:9:18:6 | Uses Step | -| .github/workflows/direct_cache6.yml:14:9:18:6 | Uses Step | .github/workflows/direct_cache6.yml:18:9:25:6 | Uses Step: cache-pip | -| .github/workflows/direct_cache6.yml:18:9:25:6 | Uses Step: cache-pip | .github/workflows/direct_cache6.yml:25:9:30:6 | Uses Step | -| .github/workflows/direct_cache6.yml:25:9:30:6 | Uses Step | .github/workflows/direct_cache6.yml:30:9:35:36 | Uses Step | +| .github/workflows/direct_cache6.yml:13:9:16:6 | Uses Step | .github/workflows/direct_cache6.yml:16:9:20:6 | Uses Step | +| .github/workflows/direct_cache6.yml:16:9:20:6 | Uses Step | .github/workflows/direct_cache6.yml:20:9:26:46 | Uses Step: cache-pip | | .github/workflows/neg_direct_cache1.yml:14:9:17:6 | Uses Step | .github/workflows/neg_direct_cache1.yml:17:9:21:6 | Uses Step | | .github/workflows/neg_direct_cache1.yml:17:9:21:6 | Uses Step | .github/workflows/neg_direct_cache1.yml:21:9:22:21 | Run Step | | .github/workflows/neg_direct_cache2.yml:14:9:17:6 | Uses Step | .github/workflows/neg_direct_cache2.yml:17:9:21:6 | Uses Step | @@ -24,6 +22,12 @@ edges | .github/workflows/neg_direct_cache3.yml:14:9:18:6 | Uses Step | .github/workflows/neg_direct_cache3.yml:18:9:25:6 | Uses Step: cache-pip | | .github/workflows/neg_direct_cache3.yml:18:9:25:6 | Uses Step: cache-pip | .github/workflows/neg_direct_cache3.yml:25:9:30:6 | Uses Step | | .github/workflows/neg_direct_cache3.yml:25:9:30:6 | Uses Step | .github/workflows/neg_direct_cache3.yml:30:9:35:36 | Uses Step | +| .github/workflows/neg_direct_cache4.yml:10:9:13:6 | Uses Step: comment-branch | .github/workflows/neg_direct_cache4.yml:13:9:18:6 | Uses Step | +| .github/workflows/neg_direct_cache4.yml:13:9:18:6 | Uses Step | .github/workflows/neg_direct_cache4.yml:18:9:22:6 | Uses Step | +| .github/workflows/neg_direct_cache4.yml:18:9:22:6 | Uses Step | .github/workflows/neg_direct_cache4.yml:22:9:23:21 | Run Step | +| .github/workflows/neg_direct_cache5.yml:10:9:13:6 | Uses Step: comment-branch | .github/workflows/neg_direct_cache5.yml:13:9:18:6 | Uses Step | +| .github/workflows/neg_direct_cache5.yml:13:9:18:6 | Uses Step | .github/workflows/neg_direct_cache5.yml:18:9:22:6 | Uses Step | +| .github/workflows/neg_direct_cache5.yml:18:9:22:6 | Uses Step | .github/workflows/neg_direct_cache5.yml:22:9:23:21 | Run Step | | .github/workflows/neg_poisonable_step1.yml:11:9:14:6 | Uses Step: comment-branch | .github/workflows/neg_poisonable_step1.yml:14:9:19:6 | Uses Step | | .github/workflows/neg_poisonable_step1.yml:14:9:19:6 | Uses Step | .github/workflows/neg_poisonable_step1.yml:19:9:20:30 | Run Step | | .github/workflows/neg_poisonable_step2.yml:13:9:16:6 | Uses Step | .github/workflows/neg_poisonable_step2.yml:16:9:17:54 | Run Step | @@ -45,4 +49,4 @@ edges | .github/workflows/direct_cache3.yml:19:9:23:6 | Uses Step | .github/workflows/direct_cache3.yml:14:9:19:6 | Uses Step | .github/workflows/direct_cache3.yml:19:9:23:6 | Uses Step | Potential cache poisoning in the context of the default branch due to privilege checkout of untrusted code. | | .github/workflows/direct_cache4.yml:17:9:21:6 | Uses Step | .github/workflows/direct_cache4.yml:14:9:17:6 | Uses Step | .github/workflows/direct_cache4.yml:17:9:21:6 | Uses Step | Potential cache poisoning in the context of the default branch due to privilege checkout of untrusted code. | | .github/workflows/direct_cache5.yml:17:9:21:6 | Uses Step | .github/workflows/direct_cache5.yml:14:9:17:6 | Uses Step | .github/workflows/direct_cache5.yml:17:9:21:6 | Uses Step | Potential cache poisoning in the context of the default branch due to privilege checkout of untrusted code. | -| .github/workflows/direct_cache6.yml:18:9:25:6 | Uses Step: cache-pip | .github/workflows/direct_cache6.yml:25:9:30:6 | Uses Step | .github/workflows/direct_cache6.yml:18:9:25:6 | Uses Step: cache-pip | Potential cache poisoning in the context of the default branch due to downloading an untrusted artifact. | +| .github/workflows/direct_cache6.yml:20:9:26:46 | Uses Step: cache-pip | .github/workflows/direct_cache6.yml:13:9:16:6 | Uses Step | .github/workflows/direct_cache6.yml:20:9:26:46 | Uses Step: cache-pip | Potential cache poisoning in the context of the default branch due to privilege checkout of untrusted code. | diff --git a/ql/test/query-tests/Security/CWE-349/CachePoisoningViaPoisonableStep.expected b/ql/test/query-tests/Security/CWE-349/CachePoisoningViaPoisonableStep.expected index a515bd87334..cc5ce9bdf87 100644 --- a/ql/test/query-tests/Security/CWE-349/CachePoisoningViaPoisonableStep.expected +++ b/ql/test/query-tests/Security/CWE-349/CachePoisoningViaPoisonableStep.expected @@ -12,10 +12,8 @@ edges | .github/workflows/direct_cache4.yml:17:9:21:6 | Uses Step | .github/workflows/direct_cache4.yml:21:9:22:21 | Run Step | | .github/workflows/direct_cache5.yml:14:9:17:6 | Uses Step | .github/workflows/direct_cache5.yml:17:9:21:6 | Uses Step | | .github/workflows/direct_cache5.yml:17:9:21:6 | Uses Step | .github/workflows/direct_cache5.yml:21:9:22:21 | Run Step | -| .github/workflows/direct_cache6.yml:13:9:14:6 | Uses Step | .github/workflows/direct_cache6.yml:14:9:18:6 | Uses Step | -| .github/workflows/direct_cache6.yml:14:9:18:6 | Uses Step | .github/workflows/direct_cache6.yml:18:9:25:6 | Uses Step: cache-pip | -| .github/workflows/direct_cache6.yml:18:9:25:6 | Uses Step: cache-pip | .github/workflows/direct_cache6.yml:25:9:30:6 | Uses Step | -| .github/workflows/direct_cache6.yml:25:9:30:6 | Uses Step | .github/workflows/direct_cache6.yml:30:9:35:36 | Uses Step | +| .github/workflows/direct_cache6.yml:13:9:16:6 | Uses Step | .github/workflows/direct_cache6.yml:16:9:20:6 | Uses Step | +| .github/workflows/direct_cache6.yml:16:9:20:6 | Uses Step | .github/workflows/direct_cache6.yml:20:9:26:46 | Uses Step: cache-pip | | .github/workflows/neg_direct_cache1.yml:14:9:17:6 | Uses Step | .github/workflows/neg_direct_cache1.yml:17:9:21:6 | Uses Step | | .github/workflows/neg_direct_cache1.yml:17:9:21:6 | Uses Step | .github/workflows/neg_direct_cache1.yml:21:9:22:21 | Run Step | | .github/workflows/neg_direct_cache2.yml:14:9:17:6 | Uses Step | .github/workflows/neg_direct_cache2.yml:17:9:21:6 | Uses Step | @@ -24,6 +22,12 @@ edges | .github/workflows/neg_direct_cache3.yml:14:9:18:6 | Uses Step | .github/workflows/neg_direct_cache3.yml:18:9:25:6 | Uses Step: cache-pip | | .github/workflows/neg_direct_cache3.yml:18:9:25:6 | Uses Step: cache-pip | .github/workflows/neg_direct_cache3.yml:25:9:30:6 | Uses Step | | .github/workflows/neg_direct_cache3.yml:25:9:30:6 | Uses Step | .github/workflows/neg_direct_cache3.yml:30:9:35:36 | Uses Step | +| .github/workflows/neg_direct_cache4.yml:10:9:13:6 | Uses Step: comment-branch | .github/workflows/neg_direct_cache4.yml:13:9:18:6 | Uses Step | +| .github/workflows/neg_direct_cache4.yml:13:9:18:6 | Uses Step | .github/workflows/neg_direct_cache4.yml:18:9:22:6 | Uses Step | +| .github/workflows/neg_direct_cache4.yml:18:9:22:6 | Uses Step | .github/workflows/neg_direct_cache4.yml:22:9:23:21 | Run Step | +| .github/workflows/neg_direct_cache5.yml:10:9:13:6 | Uses Step: comment-branch | .github/workflows/neg_direct_cache5.yml:13:9:18:6 | Uses Step | +| .github/workflows/neg_direct_cache5.yml:13:9:18:6 | Uses Step | .github/workflows/neg_direct_cache5.yml:18:9:22:6 | Uses Step | +| .github/workflows/neg_direct_cache5.yml:18:9:22:6 | Uses Step | .github/workflows/neg_direct_cache5.yml:22:9:23:21 | Run Step | | .github/workflows/neg_poisonable_step1.yml:11:9:14:6 | Uses Step: comment-branch | .github/workflows/neg_poisonable_step1.yml:14:9:19:6 | Uses Step | | .github/workflows/neg_poisonable_step1.yml:14:9:19:6 | Uses Step | .github/workflows/neg_poisonable_step1.yml:19:9:20:30 | Run Step | | .github/workflows/neg_poisonable_step2.yml:13:9:16:6 | Uses Step | .github/workflows/neg_poisonable_step2.yml:16:9:17:54 | Run Step |