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
12 changes: 12 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"fantomas": {
"version": "6.2.3",
"commands": [
"fantomas"
]
}
}
}
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence
* @tonycknight
142 changes: 142 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
name: Build & Release


on:
push:
pull_request:
branches: [ main ]
workflow_dispatch:

env:
build-version-number: 0.1.${{ github.run_number }}
dotnet_version: 7.x

jobs:
sca:
name: Check SCA
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: "${{ env.dotnet_version }}"

- name: dotnet SCA
run: |
dotnet tool restore
dotnet restore
dotnet list package --vulnerable --include-transitive | tee results.log

FOUND_VULN=`grep -c 'has the following vulnerable packages' results.log` || true
FOUND_CRIT=`grep -c 'Critical' results.log` || true
FOUND_HIGH=`grep -c 'High' results.log` || true

if [[ "$FOUND_VULN" != "0" ]]
then
if [ "$FOUND_CRIT" == "0" -a "$FOUND_HIGH" == "0"]
then
echo "### Vulnerable packages found ###"
exit 0
fi
echo "### Critical/High vulnerable packages found ###"
exit 1
fi
echo "## No problems found ##"
exit 0

style-rules:
name: Check style rules
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Setup NET SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: "${{ env.dotnet_version }}"

- name: Tool restore
run: dotnet tool restore

- name: App restore
run: dotnet restore

- name: Check style
run: dotnet fantomas ./ --check

build:
name: Build
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: "${{ env.dotnet_version }}"

- name: Tool restore
run: dotnet tool restore

- name: App restore
run: dotnet restore

- name: Build
run: dotnet build -c Release





nuget-release:
name: Nuget package & release
runs-on: ubuntu-latest
needs: [ sca, build, style-rules ]

steps:
- uses: actions/checkout@v3

- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v3
with:
dotnet-version: "${{ env.dotnet_version }}"

- name: Tool restore
run: dotnet tool restore

- name: App restore
run: dotnet restore

- name: Build package for Preview
if: ${{ github.ref != 'refs/heads/main'}}
run: dotnet pack -c Release -o ./package/ -p:PackageVersion=${{ env.build-version-number }}-preview -p:Version=${{ env.build-version-number }}-preview

- name: Build package for Release
if: ${{ github.ref == 'refs/heads/main'}}
run: dotnet pack -c Release -o ./package/ -p:PackageVersion=${{ env.build-version-number }} -p:Version=${{ env.build-version-number }}

- name: Push nuget package
if: github.event_name == 'push'
run: dotnet nuget push "package/*.nupkg" --api-key ${{ secrets.NUGET_PAT }} --source "nuget.org"

gh-release:
name: gh release
runs-on: ubuntu-latest
needs: [ nuget-release ]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

steps:
- uses: actions/checkout@v3

- name: Create Release
uses: ncipollo/release-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: v${{ env.build-version-number }}
prerelease: true
generateReleaseNotes: true
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -396,3 +396,6 @@ FodyWeavers.xsd

# JetBrains Rider
*.sln.iml

launchSettings.json
package/*
70 changes: 69 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,70 @@
# pkgchk-cli
A dotnet tool for package dependency checks

A dotnet tool for package dependency checks.

`dotnet list package` is a wonderful tool, and with its `--vulnerable` switch it is essential for code provenance. If you're not famlilar with it or why it's recommended, [see this blog post](https://devblogs.microsoft.com/nuget/how-to-scan-nuget-packages-for-security-vulnerabilities/).

Unfortunately, simple integration into CI pipelines isn't feasible: the tool does not return a non-zero return code when vulnerabilities are found; and CI pipelines rely on return codes. Users are left to parse the tool's console output, and so must maintain different scripts for different environments.

There are long-lived issues on the Dotnet & Nuget boards, which seem to be stuck:
- [Dotnet issue 16852](https://github.com/dotnet/sdk/issues/16852)
- [Dotnet issue 25091](https://github.com/dotnet/sdk/issues/25091)
- [Nuget issue 11781](https://github.com/NuGet/Home/issues/11781)

So until those issues are resolved, `dotnet list package` needs some workarounds in CI pipelines.

This tool wraps `dotnet list package` and interprets the output for vulnerabilities. Anything found will return in a non-zero return code. CI integration is as easy as local use.

## Installation into your repository

Create a tool manifest for your reepository:

```dotnet new tool-manifest```

Add the tool to your repository's toolset:

```dotnet tool install pkgchk-cli```

## Use

To check for top-level dependency vulnerabilities:

```pkgchk <project|solution>```

To check for top-level and transitive dependency vulnerabilities:

```pkgchk <project|solution> --transitive```

If there's only one project or solution file in your directory, omit the `<project|solution>` argument.


## Integration within Github actions

Simply:

```
name: run SCA
runs: |
dotnet tool restore
pkgchk <project|solution> --transitive
```

## Integration within other CI platforms

Most CI platforms fail on non-zero return codes from steps.

Simply ensure your repository has `pkgchk-cli` in its tools manifest, your CI includes `nuget.org` as a package source and run:

```
dotnet tool restore
pkgchk <project|solution> --transitive
```


## Licence

`pkgchk-cli` is licenced under MIT.

`pkgchk-cli` uses [Spectre.Console](https://spectreconsole.net/) - please check their licence.

`pkgchk-cli` uses [`dotnet list package`](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-list-package) published by Microsoft.
40 changes: 40 additions & 0 deletions pkgchk-cli.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.8.34330.188
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "pkgchk-cli", "src\pkgchk-cli\pkgchk-cli.fsproj", "{8FF6F838-0619-400B-B5EA-79597E00E8B7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9030FD14-8EA0-4FD1-A8FA-2639C11CB540}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{CC70A61A-416F-48AD-9BCA-902150B48636}"
ProjectSection(SolutionItems) = preProject
.github\workflows\build.yml = .github\workflows\build.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{F2E3E9A8-73C7-4E10-A391-5ACAA235174C}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8FF6F838-0619-400B-B5EA-79597E00E8B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8FF6F838-0619-400B-B5EA-79597E00E8B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FF6F838-0619-400B-B5EA-79597E00E8B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FF6F838-0619-400B-B5EA-79597E00E8B7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8FF6F838-0619-400B-B5EA-79597E00E8B7} = {9030FD14-8EA0-4FD1-A8FA-2639C11CB540}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {59CE1C89-868D-4DAB-A9EC-BE3E1A1207EF}
EndGlobalSection
EndGlobal
33 changes: 33 additions & 0 deletions src/pkgchk-cli/Commands.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace pkgchk

open System.ComponentModel
open Spectre.Console.Cli

type PackageCheckCommandSettings() =
inherit CommandSettings()

[<CommandArgument(0, "[SOLUTION|PROJECT]")>]
[<Description("The solution or project file to check.")>]
member val ProjectPath = "" with get, set

[<CommandOption("-t|--transitives")>]
[<Description("Check transitive packages as well as top level packages.")>]
member val IncludeTransitives = false with get, set

type PackageCheckCommand() =
inherit Command<PackageCheckCommandSettings>()

override _.Execute(context, settings) =
let r =
settings.ProjectPath
|> Io.toFullPath
|> Sca.createProcess settings.IncludeTransitives
|> Sca.get

match r with
| Choice1Of2 json ->
match Sca.parse json with
| Choice1Of2 [] -> Console.returnNoVulnerabilities ()
| Choice1Of2 hits -> Console.returnVulnerabilities hits
| Choice2Of2 error -> Console.returnError error
| Choice2Of2 error -> Console.returnError error
24 changes: 24 additions & 0 deletions src/pkgchk-cli/Console.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace pkgchk

open System
open Spectre.Console

module Console =
let returnNoVulnerabilities () =
"[bold green]No vulnerabilities found![/]"
|> AnsiConsole.Markup
|> Console.Out.WriteLine

0

let returnVulnerabilities hits =
"[bold red]Vulnerabilities found![/]"
|> AnsiConsole.Markup
|> Console.Out.WriteLine

hits |> Sca.formatHits |> Console.Out.WriteLine
1

let returnError (error: string) =
Console.Error.WriteLine error
2
14 changes: 14 additions & 0 deletions src/pkgchk-cli/Io.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace pkgchk

open System
open System.IO

module Io =

let toFullPath (path: string) =
if not <| Path.IsPathRooted(path) then
let wd = Environment.CurrentDirectory

Path.Combine(wd, path)
else
path
15 changes: 15 additions & 0 deletions src/pkgchk-cli/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace pkgchk

open Spectre.Console.Cli

module Program =
[<EntryPoint>]
let main argv =
let app = CommandApp<PackageCheckCommand>()

app.Configure(fun c -> c.PropagateExceptions().ValidateExamples() |> ignore)

try
app.Run(argv)
with ex ->
Console.returnError ex.Message
2 changes: 2 additions & 0 deletions src/pkgchk-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# pkgchk-cli
A dotnet tool for package dependency checks
Loading