Search code examples
.netgrepnugetgithub-actions

Error while making GitHub Actions CI build to fail when vulnerable nuget package is found in dotnet projects


I'm checking on ways to make GitHub Actions CI build to fail when a vulnerable nuget package is found in the projects within solution. Here is the repo.

Initially, I had the following simple command in the deployment.yml:

# Check Vulnerable Nuget Packages
- name: Checking Vulnerable Nuget Packages
  run: dotnet list package --vulnerable --include-transitive

But this doesn't make the build to fail. So after some googling I found that it is possible to make the build fail when vulnerable packages are found as per the following article. So I modified the above yml as follows:

# Check Vulnerable Nuget Packages
- name: Checking Vulnerable Nuget Packages
  run: |
    dotnet list package --vulnerable --include-transitive 2>&1 | tee build.log
    echo "Analyze dotnet vulnerable nuget package command log output..."
    grep -q -i "critical\|high\|moderate\|low" build.log; [ $? -eq 0 ] && echo "Security Vulnerabilities found in Nuget Packages on the log output" && exit 1

And here are the logs for the above command:

Run dotnet list package --vulnerable --include-transitive 2>&1 | tee build.log
  dotnet list package --vulnerable --include-transitive 2>&1 | tee build.log
  echo "Analyze dotnet vulnerable nuget package command log output..."
  grep -q -i "critical\|high\|moderate\|low" build.log; [ $? -eq 0 ] && echo "Security Vulnerabilities found in Nuget Packages on the log output" && exit 1
  shell: /usr/bin/bash -e {0}
  env:
    DOTNET_ROOT: /usr/share/dotnet

The following sources were used:
   https://api.nuget.org/v3/index.json

The given project `Web` has no vulnerable packages given the current sources.
The given project `BaseComponents` has no vulnerable packages given the current sources.
The given project `BlazorDemoComponents` has no vulnerable packages given the current sources.
The given project `SharedModels` has no vulnerable packages given the current sources.
The given project `OOPSDemoComponents` has no vulnerable packages given the current sources.
The given project `UITests` has no vulnerable packages given the current sources.
The given project `DependencyInjectionDemoComponents` has no vulnerable packages given the current sources.
The given project `SharedComponents` has no vulnerable packages given the current sources.
The given project `LINQDemoComponents` has no vulnerable packages given the current sources.
The given project `DesignPatternDemoComponents` has no vulnerable packages given the current sources.
The given project `ReportDemoComponents` has no vulnerable packages given the current sources.
The given project `HTTPClientDemoComponents` has no vulnerable packages given the current sources.
The given project `MiddlewareDemoComponents` has no vulnerable packages given the current sources.
The given project `PythonDemoComponents` has no vulnerable packages given the current sources.
The given project `SOLIDDemoComponents` has no vulnerable packages given the current sources.
The given project `TDDDemoComponents` has no vulnerable packages given the current sources.
The given project `WebAPIDemoComponents` has no vulnerable packages given the current sources.
The given project `CommonComponents` has no vulnerable packages given the current sources.
Analyze dotnet vulnerable nuget package command log output...
Security Vulnerabilities found in Nuget Packages on the log output
Error: Process completed with exit code 1.

The above logs say there are no vulnerable packages in any of the project, but the build still failed. So I decided to print the contents of the build.log to see what's wrong in it. And here is the updated command:

- name: Checking Vulnerable Nuget Packages
  run: |
    dotnet list package --vulnerable --include-transitive 2>&1 | tee build.log
    echo "printing build.log..."
    cat build.log
    echo "Analyze dotnet vulnerable nuget package command log output..."
    grep -q -i "critical\|high\|moderate\|low" build.log; [ $? -eq 0 ] && echo "Security Vulnerabilities found in Nuget Packages on the log output" && exit 1

and here is the output of the above command:

Run dotnet list package --vulnerable --include-transitive 2>&1 | tee build.log
  dotnet list package --vulnerable --include-transitive 2>&1 | tee build.log
  echo "printing build.log..."
  cat build.log
  echo "Analyze dotnet vulnerable nuget package command log output..."
  grep -q -i "critical\|high\|moderate\|low" build.log; [ $? -eq 0 ] && echo "Security Vulnerabilities found in Nuget Packages on the log output" && exit 1
  shell: /usr/bin/bash -e {0}
  env:
    DOTNET_ROOT: /usr/share/dotnet

The following sources were used:
   https://api.nuget.org/v3/index.json

The given project `Web` has no vulnerable packages given the current sources.
The given project `BaseComponents` has no vulnerable packages given the current sources.
The given project `BlazorDemoComponents` has no vulnerable packages given the current sources.
The given project `SharedModels` has no vulnerable packages given the current sources.
The given project `OOPSDemoComponents` has no vulnerable packages given the current sources.
The given project `UITests` has no vulnerable packages given the current sources.
The given project `DependencyInjectionDemoComponents` has no vulnerable packages given the current sources.
The given project `SharedComponents` has no vulnerable packages given the current sources.
The given project `LINQDemoComponents` has no vulnerable packages given the current sources.
The given project `DesignPatternDemoComponents` has no vulnerable packages given the current sources.
The given project `ReportDemoComponents` has no vulnerable packages given the current sources.
The given project `HTTPClientDemoComponents` has no vulnerable packages given the current sources.
The given project `MiddlewareDemoComponents` has no vulnerable packages given the current sources.
The given project `PythonDemoComponents` has no vulnerable packages given the current sources.
The given project `SOLIDDemoComponents` has no vulnerable packages given the current sources.
The given project `TDDDemoComponents` has no vulnerable packages given the current sources.
The given project `WebAPIDemoComponents` has no vulnerable packages given the current sources.
The given project `CommonComponents` has no vulnerable packages given the current sources.
printing build.log...

The following sources were used:
   https://api.nuget.org/v3/index.json

The given project `Web` has no vulnerable packages given the current sources.
The given project `BaseComponents` has no vulnerable packages given the current sources.
The given project `BlazorDemoComponents` has no vulnerable packages given the current sources.
The given project `SharedModels` has no vulnerable packages given the current sources.
The given project `OOPSDemoComponents` has no vulnerable packages given the current sources.
The given project `UITests` has no vulnerable packages given the current sources.
The given project `DependencyInjectionDemoComponents` has no vulnerable packages given the current sources.
The given project `SharedComponents` has no vulnerable packages given the current sources.
The given project `LINQDemoComponents` has no vulnerable packages given the current sources.
The given project `DesignPatternDemoComponents` has no vulnerable packages given the current sources.
The given project `ReportDemoComponents` has no vulnerable packages given the current sources.
The given project `HTTPClientDemoComponents` has no vulnerable packages given the current sources.
The given project `MiddlewareDemoComponents` has no vulnerable packages given the current sources.
The given project `PythonDemoComponents` has no vulnerable packages given the current sources.
The given project `SOLIDDemoComponents` has no vulnerable packages given the current sources.
The given project `TDDDemoComponents` has no vulnerable packages given the current sources.
The given project `WebAPIDemoComponents` has no vulnerable packages given the current sources.
The given project `CommonComponents` has no vulnerable packages given the current sources.
Analyze dotnet vulnerable nuget package command log output...
Security Vulnerabilities found in Nuget Packages on the log output
Error: Process completed with exit code 1.

Still the build fails. Please can you help me understand what I'm doing wrong? Anything wrong with the grep command?

Update 1:

The answer accepted works fine in case when vulnerability is present but fails when no vulnerability is present. And thats because of the change in json structure. I have requested for update in answer.

Here is the No Vulnerabilties Found case json result.

{
  "version": 1,
  "parameters": "--vulnerable --include-transitive",
  "sources": [
    "https://api.nuget.org/v3/index.json"
  ],
  "projects": [
    {
      "path": "/home/runner/work/ilovedotnet/ilovedotnet/Web/Web.csproj"
    },
    {
      "path": "/home/runner/work/ilovedotnet/ilovedotnet/BaseComponents/BaseComponents.csproj"
    }
  ]
}

Here is the Vulnerabilities Found case json result

{
  "version": 1,
  "parameters": "--vulnerable --include-transitive",
  "sources": [
    "https://api.nuget.org/v3/index.json"
  ],
  "projects": [
    {
      "path": "/home/runner/work/NugetVulnerabilityCheck/NugetVulnerabilityCheck/NugetVulnerabilityCheck/NugetVulnerabilityCheck.csproj",
      "frameworks": [
        {
          "framework": "net6.0",
          "topLevelPackages": [
            {
              "id": "System.Net.Http",
              "requestedVersion": "4.3.3",
              "resolvedVersion": "4.3.3",
              "vulnerabilities": [
                {
                  "severity": "High",
                  "advisoryurl": "https://github.com/advisories/GHSA-7jgj-8wvc-jh57"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Here is the new sample repo used to evaluate the answers.


Solution

  • The solution with grep command i.e.:

    grep -q -i "critical\|high\|moderate\|low" build.log
    

    scans the complete build.log including:

    The following sources were used:
       https://api.nuget.org/v3/index.json
    

    and, here it matches the word "low" with "following" and that's why it's failing.

    You may tweak the regex to handle that; but in future, the component name or the log message itself may contain the strings being checked by the grep regex resulting in failure.

    A more robust solution would be to use the JSON format.

    Starting .NET SDK 7.0.200, dotnet list package subcommand offers a --format flag that may be used to generate JSON output:

    The JSON key severity can be checked with the jq command to identify the vulnerabilities.

    Here's an example (https://jqplay.org/s/Ym9kqW4LbCe):

    dotnet list package --vulnerable --include-transitive --format=json > list.json
    if jq -cre '.projects | .. | .severity? // empty' list.json; then
      echo 'Vulnerabilities found! Exiting...'
      jq . list.json
      exit 1
    else
      echo 'No vulnerabilities found!'
    fi
    

    It:

    • dumps the JSON output to list.json file
    • recursively checks for severity key under projects
    • returns zero if there are any severity KV pairs
      • in case of success, it dumps the complete JSON on the console
      • you may run the same command again to print the console output if JSON is not suitable to show on the console
    • returns non-zero exit code if there are no severity keys

    You can adjust this solution to fail your workflow for some specific severity level e.g. "Critical" by updating jq filter i.e. select(test("Critical")):

    dotnet list package --vulnerable --include-transitive --format=json > list.json
    if jq -cre '.projects | .. | .severity? // empty | select(test("Critical"))' list.json; then
      echo 'Vulnerabilities found! Exiting...'
      jq . list.json
      exit 1
    else
      echo 'No vulnerabilities found!'
    fi
    

    You can further expand this solution according to your complete use case as you deem fit e.g. you might want to add validation checks for the intermediate nodes, etc.