diff --git a/.github/workflows/on_push.yml b/.github/workflows/on_push.yml index c3e4b76e..110baacf 100644 --- a/.github/workflows/on_push.yml +++ b/.github/workflows/on_push.yml @@ -43,7 +43,7 @@ jobs: run: make vulncheck - name: Coverage - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} flags: unittests @@ -189,6 +189,7 @@ jobs: uses: ludeeus/action-shellcheck@master with: ignore_paths: 'bin/tests/libs' + ignore_names: govulncheck-with-excludes.sh - name: Setup bats uses: mig4/setup-bats@v1 diff --git a/Makefile b/Makefile index 20f790b8..05688c24 100644 --- a/Makefile +++ b/Makefile @@ -144,7 +144,7 @@ lint: install-linter .PHONY: vulncheck vulncheck: go install golang.org/x/vuln/cmd/govulncheck@latest - govulncheck ./... + ./bin/govulncheck-with-excludes.sh ./... .PHONY: test test: diff --git a/bin/govulncheck-with-excludes.sh b/bin/govulncheck-with-excludes.sh new file mode 100755 index 00000000..b0a8e4b0 --- /dev/null +++ b/bin/govulncheck-with-excludes.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +# a wrapper / replacement for "govulncheck" which allows for excluding vulnerabilities +# (https://github.com/golang/go/issues/59507) + +excludeVulns="$(jq -nc '[ + + # https://pkg.go.dev/vuln/GO-2023-1987 + "GO-2023-1987", + + empty # trailing comma hack (makes diffs smaller) +]')" +export excludeVulns + +if ! command -v govulncheck > /dev/null; then + govulncheck() { + local user; user="$(id -u):$(id -g)" + local args=( + --rm --interactive --init + --user "$user" + --env HOME=/tmp + --env GOPATH=/tmp/go + --volume govulncheck:/tmp + --env CGO_ENABLED=0 + --mount "type=bind,src=$PWD,dst=/wd,ro" + --workdir /wd + "${GOLANG_IMAGE:-golang:latest}" + sh -euc ' + go install golang.org/x/vuln/cmd/govulncheck@latest > /dev/null + exec "$GOPATH/bin/govulncheck" "$@" + ' -- + ) + docker run "${args[@]}" "$@" + } +fi + +if out="$(govulncheck "$@")"; then + printf '%s\n' "$out" + exit 0 +fi + +json="$(govulncheck -json "$@")" + +vulns="$(jq <<<"$json" -cs 'map(select(has("osv")) | .osv)')" +if [ "$(jq <<<"$vulns" -r 'length')" -le 0 ]; then + printf '%s\n' "$out" + exit 1 +fi + +filtered="$(jq <<<"$vulns" -c ' + (env.excludeVulns | fromjson) as $exclude + | map(select( + .id as $id + | $exclude | index($id) | not + )) +')" + +text="$(jq <<<"$filtered" -r 'map("- \(.id) (aka \(.aliases | join(", ")))\n\n\t\(.details | gsub("\n"; "\n\t"))") | join("\n\n")')" + +if [ -z "$text" ]; then + printf 'No vulnerabilities found.\n' + exit 0 +else + printf '%s\n' "$text" + exit 1 +fi diff --git a/pkg/api/transport.go b/pkg/api/transport.go index e6f3774a..1ec3f7c7 100644 --- a/pkg/api/transport.go +++ b/pkg/api/transport.go @@ -9,43 +9,8 @@ import ( "github.com/wakatime/wakatime-cli/pkg/log" ) -const serverName = "api.wakatime.com" - -// NewTransport initializes a new http.Transport. -func NewTransport() *http.Transport { - return &http.Transport{ - ForceAttemptHTTP2: true, - MaxConnsPerHost: 1, - MaxIdleConns: 1, - MaxIdleConnsPerHost: 1, - Proxy: nil, - TLSHandshakeTimeout: DefaultTimeoutSecs * time.Second, - } -} - -// NewTransportWithHostVerificationDisabled initializes a new http.Transport with disabled host verification. -func NewTransportWithHostVerificationDisabled() *http.Transport { - t := NewTransport() - - t.TLSClientConfig = &tls.Config{ - MinVersion: tls.VersionTLS12, - RootCAs: CACerts(), - ServerName: serverName, - } - - return t -} - -// LazyCreateNewTransport uses the client's Transport if exists, or creates a new one. -func LazyCreateNewTransport(c *Client) *http.Transport { - if c != nil && c.client != nil && c.client.Transport != nil { - return c.client.Transport.(*http.Transport).Clone() - } - - return NewTransport() -} - -const letsencryptCerts string = ` +const ( + letsencryptCerts = ` -----BEGIN CERTIFICATE----- MIIEYDCCAkigAwIBAgIQB55JKIY3b9QISMI/xjHkYzANBgkqhkiG9w0BAQsFADBP MQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFy @@ -118,6 +83,42 @@ mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= -----END CERTIFICATE----- ` + serverName = "api.wakatime.com" +) + +// NewTransport initializes a new http.Transport. +func NewTransport() *http.Transport { + return &http.Transport{ + ForceAttemptHTTP2: true, + MaxConnsPerHost: 1, + MaxIdleConns: 1, + MaxIdleConnsPerHost: 1, + Proxy: nil, + TLSHandshakeTimeout: DefaultTimeoutSecs * time.Second, + } +} + +// NewTransportWithHostVerificationDisabled initializes a new http.Transport with disabled host verification. +func NewTransportWithHostVerificationDisabled() *http.Transport { + t := NewTransport() + + t.TLSClientConfig = &tls.Config{ + MinVersion: tls.VersionTLS12, + RootCAs: CACerts(), + ServerName: serverName, + } + + return t +} + +// LazyCreateNewTransport uses the client's Transport if exists, or creates a new one. +func LazyCreateNewTransport(c *Client) *http.Transport { + if c != nil && c.client != nil && c.client.Transport != nil { + return c.client.Transport.(*http.Transport).Clone() + } + + return NewTransport() +} // CACerts returns a root cert pool with the system's cacerts and LetsEncrypt's root certs. func CACerts() *x509.CertPool { diff --git a/pkg/api/transport_windows.go b/pkg/api/transport_windows.go index 6f04892a..01ef90a4 100644 --- a/pkg/api/transport_windows.go +++ b/pkg/api/transport_windows.go @@ -4,11 +4,20 @@ package api import ( "crypto/x509" + "runtime/debug" "syscall" "unsafe" + + "github.com/wakatime/wakatime-cli/pkg/log" ) func loadSystemRoots() (*x509.CertPool, error) { + defer func() { + if err := recover(); err != nil { + log.Errorf("failed to load system roots on Windows. panicked: %v. Stack: %s", err, string(debug.Stack())) + } + }() + const cryptENotFound = 0x80092004 rootPtr, err := syscall.UTF16PtrFromString("ROOT")