CVE-2023-30623: Improper Neutralization of Special Elements used in a Command ('Command Injection') (CWE-77)
value: `embano1/wip` is a GitHub Action written in Bash. Prior to version 2, the  `embano1/wip` action uses the `github.event.pull_request.title` parameter in an insecure way. The title parameter is used in a run statement - resulting in a command injection vulnerability due to string interpolation. This vulnerability can be triggered by any user on GitHub. They just need to create a pull request with a commit message containing an exploit. (Note that first-time PR requests will not be run - but the attacker can submit a valid PR before submitting an invalid PR). The commit can be genuine, but the commit message can be malicious. This can be used to execute code on the GitHub runners and can be used to exfiltrate any secrets used in the CI pipeline, including repository tokens. Version 2 has a fix for this issue.
CWE description: The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.
CVSS v3 base score: 8.8
Published: 2023-04-24T22:15Z
Repository: https://github.com/embano1/wip
Language: YAML
Files changed: .github/workflows/check-wip.yaml, README.md, action.yml
Commit date: 2023-04-23 08:44:55 +0200
Commit message: fix: Shell escaping issue

Signed-off-by: Michael Gasch <15986659+embano1@users.noreply.github.com>

Diff: diff --git a/.github/workflows/check-wip.yaml b/.github/workflows/check-wip.yaml
index 050e037..1601ee5 100644
--- a/.github/workflows/check-wip.yaml
+++ b/.github/workflows/check-wip.yaml
@@ -7,6 +7,8 @@ on:
 jobs:
   wip:
     runs-on: ubuntu-latest
     steps:
       - name: Check WIP in PR Title
-        uses: embano1/wip@v1
\ No newline at end of file
+        uses: embano1/wip@v2
+        env:
+          TITLE: ${{ github.event.pull_request.title }}
\ No newline at end of file
diff --git a/README.md b/README.md
index 3fdfa26..0adb25f 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,12 @@ work-in-progress ("WIP") patterns using regular expressions in `BASH`.
 
 # Usage
 
 ## Simple with Defaults
 
+Uses `^[[:space:]]*(WIP)+(:)*` regex pattern to match against the PR title.
+
 ```yaml
 name: Check "WIP" in PR Title
 
 on:
   pull_request:
@@ -17,11 +19,11 @@ on:
 jobs:
   check:
     runs-on: ubuntu-latest
     steps:
       - name: Check WIP in PR Title
-        uses: embano1/wip@v1
+        uses: embano1/wip@v2
 ```
 
 ## Custom Options
 
 ```yaml
@@ -34,24 +36,24 @@ on:
 jobs:
   check:
     runs-on: ubuntu-latest
     steps:
       - name: Check WIP in PR Title
-        uses: embano1/wip@v1
+        uses: embano1/wip@v2
         with:
           # this is also the default value
           title: "${{ github.event.pull_request.title }}"
 
-          # only matches PRs where title is "WIP" only
+          # only matches PRs where title is exactly "WIP"
           regex: "^WIP$" 
 
 ```
 
 ## Configuration Options
 
-| Input   | Type     | Required | Default                                  | Description                                                                                                                                                                   |
-|---------|----------|----------|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `title` | `string` | yes      | `${{ github.event.pull_request.title }}` | The title to perform regular expression pattern matching against. Typically a field from the Github [`context`](https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context) is used.                                                                                                              |
-| `regex` | `string` | yes      | `^[[:space:]]*(WIP)+(:)*`                | The regular expression to perform. The default value matches the word `WIP` (optionally followed by `:`) and ignores any whitespace character(s) at the beginning of the text |
+| Input   | Type     | Required | Default                                  | Description                                                                                                                                                                                                                            |
+|---------|----------|----------|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `title` | `string` | yes      | `${{ github.event.pull_request.title }}` | The title to perform regular expression pattern matching against. Typically a field from the Github [`context`](https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context) is used. |
+| `regex` | `string` | yes      | `^[[:space:]]*(WIP)+(:)*`                | The regular expression to perform. The default value matches the word `WIP` (optionally followed by `:`) and ignores any whitespace character(s) at the beginning of the text.                                                          |
 
-⚠️ Currently regular expression pattern matching is **case-insensitive**, i.e.
-`wip` would also match.
\ No newline at end of file
+> **Note**  
+> Currently regular expression pattern matching is **case-insensitive**, i.e., `wip` would also match.
\ No newline at end of file
diff --git a/action.yml b/action.yml
index 4220682..589bcb1 100644
--- a/action.yml
+++ b/action.yml
@@ -11,25 +11,53 @@ inputs:
   regex:
     description: "Regex pattern to match in title"
     required: true
     # starts with zero or more leading whitespace chars, WIP and zero or more colons
     default: "^[[:space:]]*(WIP)+(:)*"
+  debug:
+    description: "Enable verbose logging"
+    required: false
+    default: "false"
 runs:
   using: "composite"
   steps:
     - shell: bash
+      env:
+        TITLE: ${{ inputs.title }}
+        REGEX: ${{ inputs.regex }}
+        DEBUG: ${{ inputs.debug }}
       run: |
-        set -ex
+        set -e
 
+        if [[ ${DEBUG} == "true" ]]; then 
+          echo "Enabling debug output"
+          set -x
+        fi
+        
         # TODO (@mgasch): make configurable
         # case-insensitive
         shopt -s nocasematch
-
-        if [[ '${{ inputs.title }}' =~ ${{ inputs.regex }} ]]; then
+        
+        if [[ -z ${TITLE} ]]; then 
+          echo "::error::title input must be set";
+          exit 1
+        fi
+        
+        if [[ -z ${REGEX} ]]; then 
+          echo "::error::regex input must be set";
+          exit 1
+        fi
+        
+        if [[ ${TITLE} =~ ${REGEX} ]]; then
           echo "::error::Title marked as work in progress"
           exit 1
         else
-          echo "::debug::Title not marked as work in progress"
+          echo "Title not marked as work in progress"
         fi
-
+        
         # unset nocasematch option
         shopt -u nocasematch
+
+        if [[ ${DEBUG} == "true" ]]; then 
+          echo "Disabling debug output"
+          set +x
+        fi

Vulnerable code:         uses: embano1/wip@v1
        uses: embano1/wip@v1
        uses: embano1/wip@v1
          # only matches PRs where title is "WIP" only
| Input   | Type     | Required | Default                                  | Description                                                                                                                                                                   |
|---------|----------|----------|------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `title` | `string` | yes      | `${{ github.event.pull_request.title }}` | The title to perform regular expression pattern matching against. Typically a field from the Github [`context`](https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context) is used.                                                                                                              |
| `regex` | `string` | yes      | `^[[:space:]]*(WIP)+(:)*`                | The regular expression to perform. The default value matches the word `WIP` (optionally followed by `:`) and ignores any whitespace character(s) at the beginning of the text |
⚠️ Currently regular expression pattern matching is **case-insensitive**, i.e.
`wip` would also match.
        set -ex

        if [[ '${{ inputs.title }}' =~ ${{ inputs.regex }} ]]; then
          echo "::debug::Title not marked as work in progress"

Fixed code:         uses: embano1/wip@v2
        env:
          TITLE: ${{ github.event.pull_request.title }}
Uses `^[[:space:]]*(WIP)+(:)*` regex pattern to match against the PR title.

        uses: embano1/wip@v2
        uses: embano1/wip@v2
          # only matches PRs where title is exactly "WIP"
| Input   | Type     | Required | Default                                  | Description                                                                                                                                                                                                                            |
|---------|----------|----------|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `title` | `string` | yes      | `${{ github.event.pull_request.title }}` | The title to perform regular expression pattern matching against. Typically a field from the Github [`context`](https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#github-context) is used. |
| `regex` | `string` | yes      | `^[[:space:]]*(WIP)+(:)*`                | The regular expression to perform. The default value matches the word `WIP` (optionally followed by `:`) and ignores any whitespace character(s) at the beginning of the text.                                                          |
> **Note**  
> Currently regular expression pattern matching is **case-insensitive**, i.e., `wip` would also match.
  debug:
    description: "Enable verbose logging"
    required: false
    default: "false"
      env:
        TITLE: ${{ inputs.title }}
        REGEX: ${{ inputs.regex }}
        DEBUG: ${{ inputs.debug }}
        set -e
        if [[ ${DEBUG} == "true" ]]; then 
          echo "Enabling debug output"
          set -x
        fi
        
        
        if [[ -z ${TITLE} ]]; then 
          echo "::error::title input must be set";
          exit 1
        fi
        
        if [[ -z ${REGEX} ]]; then 
          echo "::error::regex input must be set";
          exit 1
        fi
        
        if [[ ${TITLE} =~ ${REGEX} ]]; then
          echo "Title not marked as work in progress"
        

        if [[ ${DEBUG} == "true" ]]; then 
          echo "Disabling debug output"
          set +x
        fi