Skip to content
Merged
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
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
Loading