diff --git a/assets/queries/cicd/github/run_block_injection/metadata.json b/assets/queries/cicd/github/run_block_injection/metadata.json new file mode 100644 index 00000000000..c2716ad0a84 --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/metadata.json @@ -0,0 +1,11 @@ +{ + "id": "20f14e1a-a899-4e79-9f09-b6a84cd4649b", + "queryName": "Run Block Injection", + "severity": "HIGH", + "category": "Insecure Configurations", + "descriptionText": "GitHub Actions workflows can be triggered by a variety of events. Every workflow trigger is provided with a GitHub context that contains information about the triggering event, such as which user triggered it, the branch name, and other event context details. Some of this event data, like the base repository name, hash value of a changeset, or pull request number, is unlikely to be controlled or used for injection by the user that triggered the event.", + "descriptionUrl": "https://securitylab.github.com/research/github-actions-untrusted-input/", + "platform": "CICD", + "descriptionID": "02044a75", + "cloudProvider": "common" +} diff --git a/assets/queries/cicd/github/run_block_injection/query.rego b/assets/queries/cicd/github/run_block_injection/query.rego new file mode 100644 index 00000000000..ae9a223c10e --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/query.rego @@ -0,0 +1,186 @@ +package Cx + +import data.generic.common as common_lib + +CxPolicy[result] { + + input.document[i].on["pull_request_target"] + run := input.document[i].jobs[j].steps[k].run + + patterns := [ + "github.head_ref", + "github.event.pull_request.body", + "github.event.pull_request.head.label", + "github.event.pull_request.head.ref", + "github.event.pull_request.head.repo.default_branch", + "github.event.pull_request.head.repo.description", + "github.event.pull_request.head.repo.homepage", + "github.event.pull_request.title" + ] + + matched = containsPatterns(run, patterns) + + result := { + "documentId": input.document[i].id, + "searchKey": sprintf("run={{%s}}", [run]), + "issueType": "IncorrectValue", + "keyExpectedValue": "Run block does not contain dangerous input controlled by user.", + "keyActualValue": "Run block contains dangerous input controlled by user.", + "searchLine": common_lib.build_search_line(["jobs", j, "steps", k, "run"],[]), + "searchValue": matched[m] + } +} + +CxPolicy[result] { + + input.document[i].on["issues"] + run := input.document[i].jobs[j].steps[k].run + + patterns := [ + "github.event.issue.body", + "github.event.issue.title" + ] + + matched = containsPatterns(run, patterns) + + result := { + "documentId": input.document[i].id, + "searchKey": sprintf("run={{%s}}", [run]), + "issueType": "IncorrectValue", + "keyExpectedValue": "Run block does not contain dangerous input controlled by user.", + "keyActualValue": "Run block contains dangerous input controlled by user.", + "searchLine": common_lib.build_search_line(["jobs", j, "steps", k, "run"],[]), + "searchValue": matched[m] + } +} + +CxPolicy[result] { + + input.document[i].on["issue_comment"] + run := input.document[i].jobs[j].steps[k].run + + patterns := [ + "github.event.comment.body", + "github.event.issue.body", + "github.event.issue.title" + ] + + matched = containsPatterns(run, patterns) + + result := { + "documentId": input.document[i].id, + "searchKey": sprintf("run={{%s}}", [run]), + "issueType": "IncorrectValue", + "keyExpectedValue": "Run block does not contain dangerous input controlled by user.", + "keyActualValue": "Run block contains dangerous input controlled by user.", + "searchLine": common_lib.build_search_line(["jobs", j, "steps", k, "run"],[]), + "searchValue": matched[m] + } +} + +CxPolicy[result] { + + input.document[i].on["discussion"] + run := input.document[i].jobs[j].steps[k].run + + patterns := [ + "github.event.discussion.body", + "github.event.discussion.title" + ] + + matched = containsPatterns(run, patterns) + + result := { + "documentId": input.document[i].id, + "searchKey": sprintf("run={{%s}}", [run]), + "issueType": "IncorrectValue", + "keyExpectedValue": "Run block does not contain dangerous input controlled by user.", + "keyActualValue": "Run block contains dangerous input controlled by user.", + "searchLine": common_lib.build_search_line(["jobs", j, "steps", k, "run"],[]), + "searchValue": matched[m] + } +} + +CxPolicy[result] { + + input.document[i].on["discussion_comment"] + run := input.document[i].jobs[j].steps[k].run + + patterns := [ + "github.event.comment.body", + "github.event.discussion.body", + "github.event.discussion.title" + ] + + matched = containsPatterns(run, patterns) + + result := { + "documentId": input.document[i].id, + "searchKey": sprintf("run={{%s}}", [run]), + "issueType": "IncorrectValue", + "keyExpectedValue": "Run block does not contain dangerous input controlled by user.", + "keyActualValue": "Run block contains dangerous input controlled by user.", + "searchLine": common_lib.build_search_line(["jobs", j, "steps", k, "run"],[]), + "searchValue": matched[m] + } +} + +CxPolicy[result] { + + input.document[i].on["workflow_run"] + run := input.document[i].jobs[j].steps[k].run + + patterns := [ + "github.event.workflow.path", + "github.event.workflow_run.head_branch", + "github.event.workflow_run.head_commit.author.email", + "github.event.workflow_run.head_commit.author.name", + "github.event.workflow_run.head_commit.message", + "github.event.workflow_run.head_repository.description" + ] + + matched = containsPatterns(run, patterns) + + result := { + "documentId": input.document[i].id, + "searchKey": sprintf("run={{%s}}", [run]), + "issueType": "IncorrectValue", + "keyExpectedValue": "Run block does not contain dangerous input controlled by user.", + "keyActualValue": "Run block contains dangerous input controlled by user.", + "searchLine": common_lib.build_search_line(["jobs", j, "steps", k, "run"],[]), + "searchValue": matched[m] + } +} + +CxPolicy[result] { + + input.document[i].on["author"] + run := input.document[i].jobs[j].steps[k].run + + patterns := [ + "github.*.authors.name", + "github.*.authors.email" + ] + + matched = containsPatterns(run, patterns) + + result := { + "documentId": input.document[i].id, + "searchKey": sprintf("run={{%s}}", [run]), + "issueType": "IncorrectValue", + "keyExpectedValue": "Run block does not contain dangerous input controlled by user.", + "keyActualValue": "Run block contains dangerous input controlled by user.", + "searchLine": common_lib.build_search_line(["jobs", j, "steps", k, "run"],[]), + "searchValue": matched[m] + } +} + + + +containsPatterns(str, patterns) = matched { + matched := {pattern | + pattern := patterns[_] + regex.match(pattern, str) + } +} + diff --git a/assets/queries/cicd/github/run_block_injection/test/negative.yaml b/assets/queries/cicd/github/run_block_injection/test/negative.yaml new file mode 100644 index 00000000000..5f9d4a2dfd5 --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/negative.yaml @@ -0,0 +1,29 @@ +name: check-go-coverage + +on: + pull_request_target: + branches: [master] + +jobs: + coverage: + name: Check Go coverage + runs-on: ubuntu-latest + steps: + - name: Checkout Source + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Set up Go 1.20.x + uses: actions/setup-go@v4 + with: + go-version: 1.20.x + - name: Run test metrics script + id: testcov + run: | + make test-coverage-report | tee test-results + echo "coverage=$(cat test-results | grep "Total coverage: " test-results | cut -d ":" -f 2 | bc)" >> $GITHUB_ENV + - name: Checks if Go coverage is at least 80% + if: env.coverage < 80 + run: | + echo "Go coverage is lower than 80%: ${{ env.coverage }}%" + exit 1 \ No newline at end of file diff --git a/assets/queries/cicd/github/run_block_injection/test/negative2.yaml b/assets/queries/cicd/github/run_block_injection/test/negative2.yaml new file mode 100644 index 00000000000..c954704a14f --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/negative2.yaml @@ -0,0 +1,15 @@ +name: Issue Workflow + +on: + issues: + types: + - opened + +jobs: + process_issue: + runs-on: ubuntu-latest + steps: + - name: Greet the New Issue + run: | + # Echo a simple sentence + echo "Hello, a new issue has been opened!" diff --git a/assets/queries/cicd/github/run_block_injection/test/negative3.yaml b/assets/queries/cicd/github/run_block_injection/test/negative3.yaml new file mode 100644 index 00000000000..499acd8c7ba --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/negative3.yaml @@ -0,0 +1,14 @@ +name: Discussion Workflow + +on: + discussion: + types: + - created + +jobs: + process_discussion: + runs-on: ubuntu-latest + steps: + - name: Greet the New Discussion + run: | + echo "Hello, a new discussion has been created!" diff --git a/assets/queries/cicd/github/run_block_injection/test/negative4.yaml b/assets/queries/cicd/github/run_block_injection/test/negative4.yaml new file mode 100644 index 00000000000..dd99cdf9131 --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/negative4.yaml @@ -0,0 +1,14 @@ +name: Issue Comment Workflow + +on: + issue_comment: + types: + - created + +jobs: + process_issue_comment: + runs-on: ubuntu-latest + steps: + - name: Greet the New Issue Comment + run: | + echo "Hello, a new issue comment has been created!" diff --git a/assets/queries/cicd/github/run_block_injection/test/negative5.yaml b/assets/queries/cicd/github/run_block_injection/test/negative5.yaml new file mode 100644 index 00000000000..1fc2f915b03 --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/negative5.yaml @@ -0,0 +1,14 @@ +name: Discussion Comment Workflow + +on: + discussion_comment: + types: + - created + +jobs: + process_discussion_comment: + runs-on: ubuntu-latest + steps: + - name: Greet the New Discussion Comment + run: | + echo "Hello, a new discussion comment has been created!" diff --git a/assets/queries/cicd/github/run_block_injection/test/negative6.yaml b/assets/queries/cicd/github/run_block_injection/test/negative6.yaml new file mode 100644 index 00000000000..bcb86ef0aa1 --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/negative6.yaml @@ -0,0 +1,14 @@ +name: Author Workflow + +on: + author: + types: + - created + +jobs: + process_author: + runs-on: ubuntu-latest + steps: + - name: Greet the New Author + run: | + echo "Hello, a new author has been created!" diff --git a/assets/queries/cicd/github/run_block_injection/test/negative7.yaml b/assets/queries/cicd/github/run_block_injection/test/negative7.yaml new file mode 100644 index 00000000000..57af861e9b0 --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/negative7.yaml @@ -0,0 +1,14 @@ +name: Workflow Run Workflow + +on: + workflow_run: + workflows: + - "Your Workflow Name" # Replace with the name of your specific workflow + +jobs: + process_workflow_run: + runs-on: ubuntu-latest + steps: + - name: Greet the New Workflow Run + run: | + echo "Hello, a new workflow run has started for 'Your Workflow Name'!" diff --git a/assets/queries/cicd/github/run_block_injection/test/positive1.yaml b/assets/queries/cicd/github/run_block_injection/test/positive1.yaml new file mode 100644 index 00000000000..6ee6d54c544 --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/positive1.yaml @@ -0,0 +1,39 @@ +name: Web Page To Markdown +on: + issues: + types: [opened] +jobs: + WebPageToMarkdown: + runs-on: ubuntu-latest + steps: + - name: Does the issue need to be converted to markdown + run: | + if [ "${{ github.event.issue.body }}" ]; then + if [[ "${{ github.event.issue.title }}" =~ ^\[Auto\]* ]]; then + : + else + echo "This issue does not need to generate a markdown file." 1>&2 + exit 1; + fi; + else + echo "The description of the issue is empty." 1>&2 + exit 1; + fi; + shell: bash + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + - name: Crawl pages and generate Markdown files + uses: freeCodeCamp-China/article-webpage-to-markdown-action@v0.1.8 + with: + newsLink: '${{ github.event.issue.Body }}' + markDownFilePath: './chinese/articles/' + githubToken: ${{ github.token }} + - name: Git Auto Commit + uses: stefanzweifel/git-auto-commit-action@v4.9.2 + with: + commit_message: '${{ github.event.issue.title }}' + file_pattern: chinese/articles/*.md + commit_user_name: PageToMarkdown Bot + commit_user_email: PageToMarkdown-bot@freeCodeCamp.org \ No newline at end of file diff --git a/assets/queries/cicd/github/run_block_injection/test/positive2.yaml b/assets/queries/cicd/github/run_block_injection/test/positive2.yaml new file mode 100644 index 00000000000..e8db43feddd --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/positive2.yaml @@ -0,0 +1,15 @@ +name: Pull Request Workflow + +on: + pull_request_target: + types: + - opened + +jobs: + process_pull_request: + runs-on: ubuntu-latest + steps: + - name: Echo Pull Request Body + run: | + echo "Pull Request Body: ${{ github.event.pull_request.body }}" + diff --git a/assets/queries/cicd/github/run_block_injection/test/positive3.yaml b/assets/queries/cicd/github/run_block_injection/test/positive3.yaml new file mode 100644 index 00000000000..a0fd8865e47 --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/positive3.yaml @@ -0,0 +1,14 @@ +name: Issue Comment Workflow + +on: + issue_comment: + types: + - created + +jobs: + process_issue_comment: + runs-on: ubuntu-latest + steps: + - name: Echo Issue Comment Body + run: | + echo "Issue Comment Body: ${{ github.event.comment.body }}" diff --git a/assets/queries/cicd/github/run_block_injection/test/positive4.yaml b/assets/queries/cicd/github/run_block_injection/test/positive4.yaml new file mode 100644 index 00000000000..7552ddffc8b --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/positive4.yaml @@ -0,0 +1,14 @@ +name: Discussion Workflow + +on: + discussion: + types: + - created + +jobs: + process_discussion: + runs-on: ubuntu-latest + steps: + - name: Echo Discussion Title + run: | + echo "Discussion Title: ${{ github.event.discussion.title }}" diff --git a/assets/queries/cicd/github/run_block_injection/test/positive5.yaml b/assets/queries/cicd/github/run_block_injection/test/positive5.yaml new file mode 100644 index 00000000000..39beeb868a1 --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/positive5.yaml @@ -0,0 +1,14 @@ +name: Discussion Comment Workflow + +on: + discussion_comment: + types: + - created + +jobs: + process_discussion_comment: + runs-on: ubuntu-latest + steps: + - name: Echo Discussion Comment Body + run: | + echo "Discussion Comment Body: ${{ github.event.comment.body }}" diff --git a/assets/queries/cicd/github/run_block_injection/test/positive6.yaml b/assets/queries/cicd/github/run_block_injection/test/positive6.yaml new file mode 100644 index 00000000000..ec3c8e5c96b --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/positive6.yaml @@ -0,0 +1,14 @@ +name: Author Workflow + +on: + author: + types: + - created + +jobs: + process_author: + runs-on: ubuntu-latest + steps: + - name: Echo Author's Username + run: | + echo "Author's Name: ${{ github.event.authors.name }}" diff --git a/assets/queries/cicd/github/run_block_injection/test/positive7.yaml b/assets/queries/cicd/github/run_block_injection/test/positive7.yaml new file mode 100644 index 00000000000..f86dd89b24e --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/positive7.yaml @@ -0,0 +1,14 @@ +name: Workflow Run Workflow + +on: + workflow_run: + workflows: + - "Your Workflow Name" # Replace with the name of your specific workflow + +jobs: + process_workflow_run: + runs-on: ubuntu-latest + steps: + - name: Echo Workflow Run Name + run: | + echo "Workflow Run Path: ${{ github.event.workflow.path }}" diff --git a/assets/queries/cicd/github/run_block_injection/test/positive_expected_result.json b/assets/queries/cicd/github/run_block_injection/test/positive_expected_result.json new file mode 100644 index 00000000000..dcf339d9a2f --- /dev/null +++ b/assets/queries/cicd/github/run_block_injection/test/positive_expected_result.json @@ -0,0 +1,50 @@ +[ + { + "queryName": "Run Block Injection", + "severity": "HIGH", + "line": 10, + "fileName": "positive1.yaml" + }, + { + "queryName": "Run Block Injection", + "severity": "HIGH", + "line": 10, + "fileName": "positive1.yaml" + }, + { + "queryName": "Run Block Injection", + "severity": "HIGH", + "line": 13, + "fileName": "positive2.yaml" + }, + { + "queryName": "Run Block Injection", + "severity": "HIGH", + "line": 13, + "fileName": "positive3.yaml" + }, + { + "queryName": "Run Block Injection", + "severity": "HIGH", + "line": 13, + "fileName": "positive4.yaml" + }, + { + "queryName": "Run Block Injection", + "severity": "HIGH", + "line": 13, + "fileName": "positive5.yaml" + }, + { + "queryName": "Run Block Injection", + "severity": "HIGH", + "line": 13, + "fileName": "positive6.yaml" + }, + { + "queryName": "Run Block Injection", + "severity": "HIGH", + "line": 13, + "fileName": "positive7.yaml" + } +] diff --git a/test/queries_content_test.go b/test/queries_content_test.go index f09c7e0f773..f17bf655dc0 100644 --- a/test/queries_content_test.go +++ b/test/queries_content_test.go @@ -55,7 +55,9 @@ var ( "../assets/queries/dockerfile/apt_get_install_pin_version_not_defined", "../assets/queries/terraform/aws/redshift_cluster_without_vpc", "../assets/queries/openAPI/general/response_code_missing", - "../assets/queries/cicd/github/script_block_injection", + "../assets/queries/cicd/github/run_block_injection", + "../assets/queries/cicd/github/script_block_injection", + } // TODO uncomment this test once all metadata are fixed