Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce gosec for Static Application Security Testing (SAST) #34

Merged
merged 7 commits into from
Dec 19, 2024
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
7 changes: 6 additions & 1 deletion .ci/check
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@ source "${SOURCE_PATH}/.ci/common.sh"

echo "> Check..."
make check
echo "All checks are passing"

# Run Static Application Security Testing (SAST) using gosec
echo "> SAST..."
make sast-report

echo -e "\nAll checks are passing"
17 changes: 17 additions & 0 deletions .ci/pipeline_definitions
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
etcd-wrapper:
base_definition:
repo:
source_labels:
- name: cloud.gardener.cnudie/dso/scanning-hints/source_analysis/v1
value:
policy: skip
comment: |
we use gosec for sast scanning. See attached log.
traits:
version:
preprocess:
Expand Down Expand Up @@ -61,6 +68,16 @@ etcd-wrapper:
image: europe-docker.pkg.dev/gardener-project/releases/gardener/etcd-wrapper
release:
nextversion: 'bump_minor'
assets:
- type: build-step-log
step_name: check
purposes:
- lint
- sast
- gosec
comment: |
we use gosec (linter) for SAST scans
see: https://github.com/securego/gosec
slack:
default_channel: 'internal_scp_workspace'
channel_cfgs:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ go.work
# Ignore downloaded binaries
/bin
/hack/tools/bin

# gosec
gosec-report.sarif
10 changes: 9 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,12 @@ revendor:

.PHONY: check
check: $(GOLANGCI_LINT)
@./hack/check.sh --golangci-lint-config=./.golangci.yaml ./internal/...
@./hack/check.sh --golangci-lint-config=./.golangci.yaml ./internal/...

.PHONY: sast
sast: $(GOSEC)
@./hack/sast.sh

.PHONY: sast-report
sast-report: $(GOSEC)
@./hack/sast.sh --gosec-report true
41 changes: 41 additions & 0 deletions hack/sast.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
#
# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors
#
# SPDX-License-Identifier: Apache-2.0

set -e

root_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." &> /dev/null && pwd )"

gosec_report="false"
gosec_report_parse_flags=""

parse_flags() {
while test $# -gt 1; do
case "$1" in
--gosec-report)
shift; gosec_report="$1"
;;
*)
echo "Unknown argument: $1"
exit 1
;;
esac
shift
done
}

parse_flags "$@"

echo "> Running gosec"
gosec --version
if [[ "$gosec_report" != "false" ]]; then
echo "Exporting report to $root_dir/gosec-report.sarif"
gosec_report_parse_flags="-track-suppressions -fmt=sarif -out=gosec-report.sarif -stdout"
fi

# exclude generated code, hack directory (where hack scripts reside)
# and tmp directory (where temporary mod files are downloaded)
# shellcheck disable=SC2086
gosec -exclude-generated -exclude-dir=hack -exclude-dir=tmp $gosec_report_parse_flags ./...
5 changes: 5 additions & 0 deletions hack/tools.mk
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
TOOLS_DIR := hack/tools
TOOLS_BIN_DIR := $(TOOLS_DIR)/bin
GOLANGCI_LINT := $(TOOLS_BIN_DIR)/golangci-lint
GOSEC := $(TOOLS_BIN_DIR)/gosec
GO_ADD_LICENSE := $(TOOLS_BIN_DIR)/addlicense

# default tool versions
GOLANGCI_LINT_VERSION ?= v1.61.0
GOSEC_VERSION ?= v2.21.4
GO_ADD_LICENSE_VERSION ?= latest

export TOOLS_BIN_DIR := $(TOOLS_BIN_DIR)
Expand All @@ -23,5 +25,8 @@ $(GOLANGCI_LINT): $(call tool_version_file,$(GOLANGCI_LINT),$(GOLANGCI_LINT_VERS
@# see https://github.com/golangci/golangci-lint/issues/1276
GOBIN=$(abspath $(TOOLS_BIN_DIR)) CGO_ENABLED=1 go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)

$(GOSEC): $(call tool_version_file,$(GOSEC),$(GOSEC_VERSION))
@GOSEC_VERSION=$(GOSEC_VERSION) $(TOOLS_DIR)/install-gosec.sh

$(GO_ADD_LICENSE):
GOBIN=$(abspath $(TOOLS_BIN_DIR)) go install github.com/google/addlicense@$(GO_ADD_LICENSE_VERSION)
41 changes: 41 additions & 0 deletions hack/tools/install-gosec.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
#
# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors
#
# SPDX-License-Identifier: Apache-2.0

set -e

echo "> Installing gosec"

TOOLS_BIN_DIR=${TOOLS_BIN_DIR:-$(dirname $0)/bin}

platform=$(uname -s | tr '[:upper:]' '[:lower:]')
version=$GOSEC_VERSION
case $(uname -m) in
aarch64 | arm64)
arch="arm64"
;;
x86_64)
arch="amd64"
;;
*)
echo "Unknown architecture"
exit 1
;;
esac

archive_name="gosec_${version#v}_${platform}_${arch}"
file_name="${archive_name}.tar.gz"

temp_dir="$(mktemp -d)"
function cleanup {
rm -rf "${temp_dir}"
}
trap cleanup EXIT ERR INT TERM

curl -L -o "${temp_dir}/${file_name}" "https://github.com/securego/gosec/releases/download/${version}/${file_name}"

tar -xzm -C "${temp_dir}" -f "${temp_dir}/${file_name}"
mv "${temp_dir}/gosec" $TOOLS_BIN_DIR
chmod +x "${TOOLS_BIN_DIR}/gosec"
16 changes: 9 additions & 7 deletions internal/app/readycheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ import (

const (
// ServerPort is the port number for the http server of etcd wrapper
ServerPort = int64(9095)
etcdConnectionTimeout = 5 * time.Second
etcdGetTimeout = 5 * time.Second
etcdQueryInterval = 2 * time.Second
etcdEndpointPort = "2379"
ServerPort = int64(9095)
etcdEndpointPort = "2379"
etcdWrapperReadHeaderTimeout = 5 * time.Second
etcdConnectionTimeout = 5 * time.Second
etcdGetTimeout = 5 * time.Second
etcdQueryInterval = 2 * time.Second
)

// queryAndUpdateEtcdReadiness periodically queries the etcd DB to check its readiness and updates the status
Expand Down Expand Up @@ -146,7 +147,8 @@ func (a *Application) RegisterHandler() {
mux.HandleFunc("/stop", a.stopEtcdHandler)

a.server = &http.Server{
Addr: fmt.Sprintf(":%d", ServerPort),
Handler: mux,
Addr: fmt.Sprintf(":%d", ServerPort),
Handler: mux,
ReadHeaderTimeout: etcdWrapperReadHeaderTimeout,
}
}
4 changes: 2 additions & 2 deletions internal/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func CaptureExitCode(signal os.Signal, exitCodeFilePath string) error {
return nil
}
interruptSignal := []byte(signal.String())
return os.WriteFile(exitCodeFilePath, interruptSignal, 0644)
return os.WriteFile(exitCodeFilePath, interruptSignal, 0600)
}

// CleanupExitCode removes the `exit_code` file
Expand Down Expand Up @@ -116,7 +116,7 @@ func (i *initializer) tryGetEtcdConfig(ctx context.Context, maxRetries int, inte
func determineValidationMode(exitCodeFilePath string, logger *zap.Logger) brclient.ValidationType {
var err error
if _, err = os.Stat(exitCodeFilePath); err == nil {
data, err := os.ReadFile(exitCodeFilePath)
data, err := os.ReadFile(exitCodeFilePath) // #nosec G304 -- only path passed is `DefaultExitCodeFilePath`, no user input is used.
if err != nil {
logger.Error("error in reading exitCodeFile, assuming full-validation to be done.", zap.String("exitCodeFilePath", exitCodeFilePath), zap.Error(err))
return brclient.FullValidation
Expand Down
2 changes: 1 addition & 1 deletion internal/brclient/brclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (c *brClient) GetEtcdConfig(ctx context.Context) (string, error) {
if err != nil {
return "", err
}
if err = os.WriteFile(c.etcdConfigFilePath, etcdConfigBytes, 0644); err != nil {
if err = os.WriteFile(c.etcdConfigFilePath, etcdConfigBytes, 0600); err != nil {
return "", err
}
return c.etcdConfigFilePath, nil
Expand Down
4 changes: 2 additions & 2 deletions internal/testutil/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ func (c *CertKeyPair) EncodeAndWrite(dir string, certFileName, keyFileName strin
key := x509.MarshalPKCS1PrivateKey(&c.PrivateKey)
pemKeyBytes := pemEncode(key, "RSA PRIVATE KEY")
privateKeyPath := filepath.Join(dir, keyFileName)
err := os.WriteFile(privateKeyPath, pemKeyBytes, os.ModePerm)
err := os.WriteFile(privateKeyPath, pemKeyBytes, 0600)
if err != nil {
return fmt.Errorf("failed to write private key to dir: %s: err: %v", dir, err)
}
pemCertBytes := pemEncode(c.CertBytes, "CERTIFICATE")
certPath := filepath.Join(dir, certFileName)
err = os.WriteFile(certPath, pemCertBytes, os.ModePerm)
err = os.WriteFile(certPath, pemCertBytes, 0600)
if err != nil {
return fmt.Errorf("failed to write certificate to dir: %s: err: %v", dir, err)
}
Expand Down
6 changes: 3 additions & 3 deletions internal/util/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

// CreateCACertPool creates a CA cert pool gives a CA cert bundle
func CreateCACertPool(caCertBundlePath string) (*x509.CertPool, error) {
caCertBundle, err := os.ReadFile(caCertBundlePath)
caCertBundle, err := os.ReadFile(caCertBundlePath) // #nosec G304 -- path is generated by etcd-backup-restore server's /config handler.
if err != nil {
return nil, err
}
Expand All @@ -35,9 +35,9 @@ type KeyPair struct {

// CreateTLSConfig creates a TLS Config to be used for TLS communication.
func CreateTLSConfig(tlsEnabledFn IsTLSEnabledFn, serverName, caCertPath string, keyPair *KeyPair) (*tls.Config, error) {
tlsConf := tls.Config{}
tlsConf := tls.Config{} // #nosec G402 -- tlsConf.MinVersion=1.2 by default.
if !tlsEnabledFn() {
tlsConf.InsecureSkipVerify = true
tlsConf.InsecureSkipVerify = true // #nosec G402 -- InsecureSkipVerify is set to true only when TLS is disabled.
return &tlsConf, nil
}

Expand Down