Skip to content

Commit

Permalink
Merge pull request #17401 from pwntester/js/actions/secrets-in-artifacts
Browse files Browse the repository at this point in the history
Javascript: Query to detect GITHUB_TOKEN leaked in artifacts
  • Loading branch information
asgerf authored Sep 11, 2024
2 parents 15cdc72 + 061d58a commit 07bd854
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 0 deletions.
30 changes: 30 additions & 0 deletions javascript/ql/src/Security/CWE-312/ActionsArtifactLeak.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Sensitive information included in a GitHub Actions artifact can allow an attacker to access
the sensitive information if the artifact is published.
</p>
</overview>

<recommendation>
<p>
Only store information that is meant to be publicly available in a GitHub Actions artifact.
</p>
</recommendation>

<example>
<p>
The following example uses <code>actions/checkout</code> to checkout code which stores the GITHUB_TOKEN in the `.git/config` file
and then stores the contents of the `.git` repository into the artifact:
</p>
<sample src="examples/actions-artifact-leak.yml"/>
<p>
The issue has been fixed below, where the <code>actions/upload-artifact</code> uses a version (v4+) which does not include hidden files or
directories into the artifact.
</p>
<sample src="examples/actions-artifact-leak-fixed.yml"/>
</example>
</qhelp>
111 changes: 111 additions & 0 deletions javascript/ql/src/Security/CWE-312/ActionsArtifactLeak.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* @name Storage of sensitive information in GitHub Actions artifact
* @description Including sensitive information in a GitHub Actions artifact can
* expose it to an attacker.
* @kind problem
* @problem.severity error
* @security-severity 7.5
* @precision high
* @id js/actions/actions-artifact-leak
* @tags security
* external/cwe/cwe-312
* external/cwe/cwe-315
* external/cwe/cwe-359
*/

import javascript
import semmle.javascript.Actions

/**
* A step that uses `actions/checkout` action.
*/
class ActionsCheckoutStep extends Actions::Step {
ActionsCheckoutStep() { this.getUses().getGitHubRepository() = "actions/checkout" }
}

/**
* A `with:`/`persist-credentials` field sibling to `uses: actions/checkout`.
*/
class ActionsCheckoutWithPersistCredentials extends YamlNode, YamlScalar {
ActionsCheckoutStep step;

ActionsCheckoutWithPersistCredentials() {
step.lookup("with").(YamlMapping).lookup("persist-credentials") = this
}

/** Gets the step this field belongs to. */
ActionsCheckoutStep getStep() { result = step }
}

/**
* A `with:`/`path` field sibling to `uses: actions/checkout`.
*/
class ActionsCheckoutWithPath extends YamlNode, YamlString {
ActionsCheckoutStep step;

ActionsCheckoutWithPath() { step.lookup("with").(YamlMapping).lookup("path") = this }

/** Gets the step this field belongs to. */
ActionsCheckoutStep getStep() { result = step }
}

/**
* A step that uses `actions/upload-artifact` action.
*/
class ActionsUploadArtifactStep extends Actions::Step {
ActionsUploadArtifactStep() { this.getUses().getGitHubRepository() = "actions/upload-artifact" }
}

/**
* A `with:`/`path` field sibling to `uses: actions/upload-artifact`.
*/
class ActionsUploadArtifactWithPath extends YamlNode, YamlString {
ActionsUploadArtifactStep step;

ActionsUploadArtifactWithPath() { step.lookup("with").(YamlMapping).lookup("path") = this }

/** Gets the step this field belongs to. */
ActionsUploadArtifactStep getStep() { result = step }
}

from ActionsCheckoutStep checkout, ActionsUploadArtifactStep upload, Actions::Job job, int i, int j
where
checkout.getJob() = job and
upload.getJob() = job and
job.getStep(i) = checkout and
job.getStep(j) = upload and
j = i + 1 and
upload.getUses().getVersion() =
[
"v4.3.6", "834a144ee995460fba8ed112a2fc961b36a5ec5a", //
"v4.3.5", "89ef406dd8d7e03cfd12d9e0a4a378f454709029", //
"v4.3.4", "0b2256b8c012f0828dc542b3febcab082c67f72b", //
"v4.3.3", "65462800fd760344b1a7b4382951275a0abb4808", //
"v4.3.2", "1746f4ab65b179e0ea60a494b83293b640dd5bba", //
"v4.3.1", "5d5d22a31266ced268874388b861e4b58bb5c2f3", //
"v4.3.0", "26f96dfa697d77e81fd5907df203aa23a56210a8", //
"v4.2.0", "694cdabd8bdb0f10b2cea11669e1bf5453eed0a6", //
"v4.1.0", "1eb3cb2b3e0f29609092a73eb033bb759a334595", //
"v4.0.0", "c7d193f32edcb7bfad88892161225aeda64e9392", //
] and
(
not exists(ActionsCheckoutWithPersistCredentials persist | persist.getStep() = checkout)
or
exists(ActionsCheckoutWithPersistCredentials persist |
persist.getStep() = checkout and
persist.getValue() = "true"
)
) and
(
not exists(ActionsCheckoutWithPath path | path.getStep() = checkout) and
exists(ActionsUploadArtifactWithPath path |
path.getStep() = upload and path.getValue() = [".", "*"]
)
or
exists(ActionsCheckoutWithPath checkout_path, ActionsUploadArtifactWithPath upload_path |
checkout_path.getValue() + ["", "/*"] = upload_path.getValue() and
checkout_path.getStep() = checkout and
upload_path.getStep() = upload
)
)
select upload, "A secret may be exposed in an artifact."
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: secrets-in-artifacts
on:
pull_request:
jobs:
a-job: # NOT VULNERABLE
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Upload artifact"
uses: actions/upload-artifact@v4
with:
name: file
path: .

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: secrets-in-artifacts
on:
pull_request:
jobs:
a-job: # VULNERABLE
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Upload artifact"
uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
with:
name: file
path: .
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
category: majorAnalysis
---

- Added a new query (`js/actions/actions-artifact-leak`) to detect GitHub Actions artifacts that may leak the GITHUB_TOKEN token.
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: secrets-in-artifacts
on:
pull_request:
jobs:
test1: # VULNERABLE
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Upload artifact"
uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
with:
name: file
path: .
test2: # NOT VULNERABLE
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Upload artifact"
uses: actions/upload-artifact@v4
with:
name: file
path: .
test3: # VULNERABLE
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Upload artifact"
uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
with:
name: file
path: "*"
test4: # VULNERABLE
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
path: foo
- name: "Upload artifact"
uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
with:
name: file
path: foo
test5: # VULNERABLE
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
path: foo
- name: "Upload artifact"
uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
with:
name: file
path: foo/*
test6: # NOT VULNERABLE
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
path: pr
- name: "Upload artifact"
uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
with:
name: file
path: foo
test7: # NOT VULNERABLE
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- name: "Upload artifact"
uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
with:
name: file
path: .
test8: # VULNERABLE
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
persist-credentials: true
- name: "Upload artifact"
uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
with:
name: file
path: .

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
| .github/workflows/test.yml:9:9:14:2 | name: " ... tifact" | A secret may be exposed in an artifact. |
| .github/workflows/test.yml:27:9:32:2 | name: " ... tifact" | A secret may be exposed in an artifact. |
| .github/workflows/test.yml:38:9:43:2 | name: " ... tifact" | A secret may be exposed in an artifact. |
| .github/workflows/test.yml:49:9:54:2 | name: " ... tifact" | A secret may be exposed in an artifact. |
| .github/workflows/test.yml:82:9:86:18 | name: " ... tifact" | A secret may be exposed in an artifact. |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Security/CWE-312/ActionsArtifactLeak.ql

0 comments on commit 07bd854

Please sign in to comment.