diff --git a/CHANGELOG.md b/CHANGELOG.md index 673bd007b..112210bf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. ## Table of Contents +- [2.15.0](#2150) - [2.14.2](#2142) - [2.14.1](#2141) - [2.14.0](#2140) @@ -34,6 +35,15 @@ All notable changes to this project will be documented in this file. --- +## `2.15.0` + +- Update support for AppArmor + - thanks @esticansat +- Resolve linting issues +- More explicit error handling +- Bump dependencies + - Including catching up to latest and dropping stale dependencies + ## `2.14.2` - Bump dependencies diff --git a/MAINTAINERS b/MAINTAINERS index fe9af9890..1bddbe7f3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1,4 +1,4 @@ Andrew Martin, ControlPlane (Twitter: @sublimino) -Stefan Prodan, Weaveworks (Twitter: @stefanprodan) +Stefan Prodan, ControlPlane (Twitter: @stefanprodan) Jon Knox, ControlPlane (Twitter: @jonknox) Jack Kelly, ControlPlane diff --git a/cmd/http.go b/cmd/http.go index 39e48d974..b45e80bb5 100644 --- a/cmd/http.go +++ b/cmd/http.go @@ -66,7 +66,7 @@ var httpCmd = &cobra.Command{ stopCh := server.SetupSignalHandler() jsonLogger, err := NewLogger("info", "json") if err != nil { - return fmt.Errorf("Unable to create new logger: %w", err) + return fmt.Errorf("unable to create new logger: %w", err) } ver := os.Getenv("K8S_SCHEMA_VER") diff --git a/cmd/main.go b/cmd/main.go index da539910f..204a6ef23 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,10 +1,12 @@ package cmd import ( + "errors" "fmt" "log" "os" "strings" + "syscall" "github.com/spf13/cobra" "go.uber.org/zap" @@ -36,8 +38,15 @@ func Execute() { if err != nil { log.Fatalf("can't initialize zap logger: %v", err) } - - defer logger.Sync() + defer func() { + if err := logger.Sync(); err != nil { + // exclude invalid argument error + // https://github.com/uber-go/zap/issues/328 + if !errors.Is(err, syscall.EINVAL) { + log.Fatalf("failed to cleanup logger: %+v", err) + } + } + }() rootCmd.SetArgs(os.Args[1:]) if err := rootCmd.Execute(); err != nil { diff --git a/cmd/print-rules.go b/cmd/print-rules.go index e2a39eaa6..1d8c0e255 100644 --- a/cmd/print-rules.go +++ b/cmd/print-rules.go @@ -36,14 +36,20 @@ func init() { printTableFn := func(w io.Writer) error { tw := util.NewTabWriter(w) - fmt.Fprintf(tw, "ID\tReason\tPoints\tKinds\n") + _, err := fmt.Fprintf(tw, "ID\tReason\tPoints\tKinds\n") + if err != nil { + logger.Fatalf("could not write to tabwriter: %v", err) + } for _, rule := range ruleSet.Rules { - fmt.Fprintf(tw, "%s\t%s\t%d\t%s\t\n", + _, err = fmt.Fprintf(tw, "%s\t%s\t%d\t%s\t\n", rule.ID, rule.Reason, rule.Points, strings.Join(rule.Kinds, ","), ) + if err != nil { + logger.Fatalf("could not write to tabwriter: %v", err) + } } return tw.Flush() } diff --git a/go.mod b/go.mod index b20363182..cf39761b0 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,16 @@ module github.com/controlplaneio/kubesec/v2 -go 1.24 +go 1.25 require ( - github.com/ghodss/yaml v1.0.0 github.com/in-toto/in-toto-golang v0.9.0 - github.com/prometheus/client_golang v1.22.0 - github.com/spf13/cobra v1.9.1 + github.com/prometheus/client_golang v1.23.2 + github.com/spf13/cobra v1.10.1 github.com/thedevsaddam/gojsonq/v2 v2.5.2 github.com/yannh/kubeconform v0.7.0 go.uber.org/zap v1.27.0 - gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.1 + sigs.k8s.io/yaml v1.6.0 ) require ( @@ -21,17 +21,16 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect - github.com/prometheus/procfs v0.17.0 // indirect + github.com/prometheus/common v0.67.2 // indirect + github.com/prometheus/procfs v0.19.2 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect - github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/spf13/pflag v1.0.10 // indirect go.uber.org/multierr v1.11.0 // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/text v0.27.0 // indirect - google.golang.org/protobuf v1.36.6 // indirect - sigs.k8s.io/yaml v1.5.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/crypto v0.44.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/text v0.31.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect ) diff --git a/go.sum b/go.sum index 0048ee4c8..d89c1475a 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,6 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -41,30 +39,30 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= -github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= -github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= +github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8= +github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= -github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= -github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= +github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g= +github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/thedevsaddam/gojsonq/v2 v2.5.2 h1:CoMVaYyKFsVj6TjU6APqAhAvC07hTI6IQen8PHzHYY0= github.com/thedevsaddam/gojsonq/v2 v2.5.2/go.mod h1:bv6Xa7kWy82uT0LnXPE2SzGqTj33TAEeR560MdJkiXs= github.com/yannh/kubeconform v0.7.0 h1:ZFfniR8VChrWQxaxTUGnNrxw8RIDkjVBrjdhXSamwjw= @@ -75,26 +73,24 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= -golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= -golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= +golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ= -sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/pkg/report/writer.go b/pkg/report/writer.go index 1c5e3a85b..6a032b8b9 100644 --- a/pkg/report/writer.go +++ b/pkg/report/writer.go @@ -37,7 +37,7 @@ func WriteReports(format string, output io.Writer, reports reports, outputTempla return err } default: - return errors.New("Unrecognized format specified") + return errors.New("unrecognized format specified") } if err := writer.Write(reports); err != nil { diff --git a/pkg/ruler/rule_test.go b/pkg/ruler/rule_test.go index cf8d91adf..56ca58674 100644 --- a/pkg/ruler/rule_test.go +++ b/pkg/ruler/rule_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/controlplaneio/kubesec/v2/pkg/rules" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) func TestRule_Eval(t *testing.T) { diff --git a/pkg/ruler/ruleset.go b/pkg/ruler/ruleset.go index f30daf303..1165e62ff 100644 --- a/pkg/ruler/ruleset.go +++ b/pkg/ruler/ruleset.go @@ -10,10 +10,10 @@ import ( "sort" "sync" - "github.com/ghodss/yaml" "github.com/in-toto/in-toto-golang/in_toto" "github.com/thedevsaddam/gojsonq/v2" "go.uber.org/zap" + "sigs.k8s.io/yaml" "github.com/controlplaneio/kubesec/v2/pkg/rules" ) diff --git a/pkg/rules/allowPrivilegeEscalation_test.go b/pkg/rules/allowPrivilegeEscalation_test.go index c32abfcf7..768686bd3 100644 --- a/pkg/rules/allowPrivilegeEscalation_test.go +++ b/pkg/rules/allowPrivilegeEscalation_test.go @@ -3,7 +3,7 @@ package rules import ( "testing" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) func Test_AllowPrivilegeEscalation(t *testing.T) { diff --git a/pkg/rules/apparmorAny_test.go b/pkg/rules/apparmorAny_test.go index b91cc8e12..e4b89a6e5 100644 --- a/pkg/rules/apparmorAny_test.go +++ b/pkg/rules/apparmorAny_test.go @@ -3,8 +3,8 @@ package rules import ( "testing" - "github.com/ghodss/yaml" "github.com/thedevsaddam/gojsonq/v2" + "sigs.k8s.io/yaml" ) func Test_ApparmorAny(t *testing.T) { diff --git a/pkg/rules/apparmorUnconfined_test.go b/pkg/rules/apparmorUnconfined_test.go index 00071eccb..bdd91da95 100644 --- a/pkg/rules/apparmorUnconfined_test.go +++ b/pkg/rules/apparmorUnconfined_test.go @@ -3,7 +3,7 @@ package rules import ( "testing" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) func Test_apparmorUnconfined(t *testing.T) { diff --git a/pkg/rules/automountServiceAccountToken.go b/pkg/rules/automountServiceAccountToken.go index 0d935669e..aac071437 100644 --- a/pkg/rules/automountServiceAccountToken.go +++ b/pkg/rules/automountServiceAccountToken.go @@ -2,21 +2,21 @@ package rules import ( "bytes" - + "github.com/thedevsaddam/gojsonq/v2" ) func AutomountServiceAccountToken(json []byte) int { spec := getSpecSelector(json) - + res := gojsonq.New().Reader(bytes.NewReader(json)). From(spec + ".automountServiceAccountToken").Get() - + if res != nil { if v, ok := res.(bool); ok && !v { return 1 } } - + return 0 } diff --git a/pkg/rules/automountServiceAccountToken_test.go b/pkg/rules/automountServiceAccountToken_test.go index 62b787723..eec707bdb 100644 --- a/pkg/rules/automountServiceAccountToken_test.go +++ b/pkg/rules/automountServiceAccountToken_test.go @@ -2,8 +2,8 @@ package rules import ( "testing" - - "github.com/ghodss/yaml" + + "sigs.k8s.io/yaml" ) func Test_AutomountServiceAccountToken_Pod(t *testing.T) { @@ -67,7 +67,7 @@ spec: want: 0, }, } - + for _, testCase := range testCases { tc := testCase t.Run(tc.name, func(t *testing.T) { @@ -226,7 +226,7 @@ spec: want: 0, }, } - + for _, testCase := range testCases { tc := testCase t.Run(tc.name, func(t *testing.T) { @@ -354,7 +354,7 @@ spec: want: 0, }, } - + for _, testCase := range testCases { tc := testCase t.Run(tc.name, func(t *testing.T) { @@ -486,7 +486,7 @@ spec: want: 0, }, } - + for _, testCase := range testCases { tc := testCase t.Run(tc.name, func(t *testing.T) { diff --git a/pkg/rules/capDropAll_test.go b/pkg/rules/capDropAll_test.go index 28f412197..f8ff30642 100644 --- a/pkg/rules/capDropAll_test.go +++ b/pkg/rules/capDropAll_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_CapDropAll_Pod(t *testing.T) { @@ -44,7 +45,7 @@ spec: - name: c1 securityContext: capabilities: - drop: + drop: - name: c2 securityContext: capabilities: diff --git a/pkg/rules/capDropAny_test.go b/pkg/rules/capDropAny_test.go index 71acf9036..c516548fa 100644 --- a/pkg/rules/capDropAny_test.go +++ b/pkg/rules/capDropAny_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_CapDropAny_Pod(t *testing.T) { @@ -45,7 +46,7 @@ spec: - name: c1 securityContext: capabilities: - drop: + drop: - name: c2 securityContext: capabilities: @@ -130,7 +131,7 @@ spec: securityContext: capabilities: drop: - - + - ` json, err := yaml.YAMLToJSON([]byte(data)) @@ -156,8 +157,8 @@ spec: - name: c1 securityContext: capabilities: - drop: - - + drop: + - ` json, err := yaml.YAMLToJSON([]byte(data)) diff --git a/pkg/rules/capSysAdmin_test.go b/pkg/rules/capSysAdmin_test.go index 1ce5435fb..c634e4303 100644 --- a/pkg/rules/capSysAdmin_test.go +++ b/pkg/rules/capSysAdmin_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_CapSysAdmin_Pod(t *testing.T) { diff --git a/pkg/rules/dockerSock.go b/pkg/rules/dockerSock.go index 6de0e0b23..b55f15936 100644 --- a/pkg/rules/dockerSock.go +++ b/pkg/rules/dockerSock.go @@ -3,20 +3,24 @@ package rules import ( "bytes" "fmt" - "github.com/thedevsaddam/gojsonq/v2" "strings" + + "github.com/thedevsaddam/gojsonq/v2" ) func DockerSock(json []byte) int { spec := getSpecSelector(json) found := 0 - paths := gojsonq.New().Reader(bytes.NewReader(json)). + data := gojsonq.New().Reader(bytes.NewReader(json)). From(spec + ".volumes"). Only("hostPath.path") - if paths != nil && strings.Contains(fmt.Sprintf("%v", paths), "/var/run/docker.sock") { - found++ + paths, ok := data.([]interface{}) + if ok && paths != nil { + if strings.Contains(fmt.Sprintf("%v", paths), "/var/run/docker.sock") { + found++ + } } return found diff --git a/pkg/rules/dockerSock_test.go b/pkg/rules/dockerSock_test.go index 6eafea414..2ea9caa38 100644 --- a/pkg/rules/dockerSock_test.go +++ b/pkg/rules/dockerSock_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_DockerSock_Pod(t *testing.T) { diff --git a/pkg/rules/helper_test.go b/pkg/rules/helper_test.go index db0c5c3eb..2d8a51894 100644 --- a/pkg/rules/helper_test.go +++ b/pkg/rules/helper_test.go @@ -3,8 +3,8 @@ package rules import ( "testing" - "github.com/ghodss/yaml" "github.com/thedevsaddam/gojsonq/v2" + "sigs.k8s.io/yaml" ) func testCheckSecurityContextRule(json []byte) int { @@ -115,7 +115,6 @@ spec: } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { json, err := yaml.YAMLToJSON([]byte(tt.data)) if err != nil { diff --git a/pkg/rules/hostAliases_test.go b/pkg/rules/hostAliases_test.go index bfe799ec8..81211c710 100644 --- a/pkg/rules/hostAliases_test.go +++ b/pkg/rules/hostAliases_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_HostAliases(t *testing.T) { diff --git a/pkg/rules/hostIPC_test.go b/pkg/rules/hostIPC_test.go index 596bac36e..f0f42219d 100644 --- a/pkg/rules/hostIPC_test.go +++ b/pkg/rules/hostIPC_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_HostIPC(t *testing.T) { diff --git a/pkg/rules/hostNetwork_test.go b/pkg/rules/hostNetwork_test.go index 978adcdcd..8b74882af 100644 --- a/pkg/rules/hostNetwork_test.go +++ b/pkg/rules/hostNetwork_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_HostNetwork(t *testing.T) { diff --git a/pkg/rules/hostPID_test.go b/pkg/rules/hostPID_test.go index d09808160..c1f345e0b 100644 --- a/pkg/rules/hostPID_test.go +++ b/pkg/rules/hostPID_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_HostPID(t *testing.T) { diff --git a/pkg/rules/hostUsers_test.go b/pkg/rules/hostUsers_test.go index eccdb90c1..b791ae573 100644 --- a/pkg/rules/hostUsers_test.go +++ b/pkg/rules/hostUsers_test.go @@ -3,7 +3,7 @@ package rules import ( "testing" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) func Test_HostUsers_Pod(t *testing.T) { diff --git a/pkg/rules/limitsCPU.go b/pkg/rules/limitsCPU.go index 0222f4182..81c50dc6d 100644 --- a/pkg/rules/limitsCPU.go +++ b/pkg/rules/limitsCPU.go @@ -10,12 +10,13 @@ func LimitsCPU(json []byte) int { spec := getSpecSelector(json) found := 0 - paths := gojsonq.New().Reader(bytes.NewReader(json)). + data := gojsonq.New().Reader(bytes.NewReader(json)). From(spec + ".containers"). Only("resources.limits.cpu") - if paths != nil { - found += len(paths.([]interface{})) + paths, ok := data.([]interface{}) + if ok && paths != nil { + found += len(paths) } return found diff --git a/pkg/rules/limitsCPU_test.go b/pkg/rules/limitsCPU_test.go index eec989123..c3541db9e 100644 --- a/pkg/rules/limitsCPU_test.go +++ b/pkg/rules/limitsCPU_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_LimitsCPU_Pod(t *testing.T) { diff --git a/pkg/rules/limitsMemory.go b/pkg/rules/limitsMemory.go index 3a25e17be..1f82477a1 100644 --- a/pkg/rules/limitsMemory.go +++ b/pkg/rules/limitsMemory.go @@ -2,6 +2,7 @@ package rules import ( "bytes" + "github.com/thedevsaddam/gojsonq/v2" ) @@ -9,12 +10,13 @@ func LimitsMemory(json []byte) int { spec := getSpecSelector(json) found := 0 - paths := gojsonq.New().Reader(bytes.NewReader(json)). + data := gojsonq.New().Reader(bytes.NewReader(json)). From(spec + ".containers"). Only("resources.limits.memory") - if paths != nil { - found += len(paths.([]interface{})) + paths, ok := data.([]interface{}) + if ok && paths != nil { + found += len(paths) } return found diff --git a/pkg/rules/limitsMemory_test.go b/pkg/rules/limitsMemory_test.go index d667eb155..d830d8c52 100644 --- a/pkg/rules/limitsMemory_test.go +++ b/pkg/rules/limitsMemory_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_LimitsMemory_Pod(t *testing.T) { diff --git a/pkg/rules/privileged_test.go b/pkg/rules/privileged_test.go index 238d85e54..e27de681b 100644 --- a/pkg/rules/privileged_test.go +++ b/pkg/rules/privileged_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_Privileged_InitContainers(t *testing.T) { diff --git a/pkg/rules/procMount.go b/pkg/rules/procMount.go index c30e95acb..2cb9b31fc 100644 --- a/pkg/rules/procMount.go +++ b/pkg/rules/procMount.go @@ -12,12 +12,15 @@ func ProcMount(json []byte) int { spec := getSpecSelector(json) found := 0 - paths := gojsonq.New().Reader(bytes.NewReader(json)). + data := gojsonq.New().Reader(bytes.NewReader(json)). From(spec + ".volumes"). Only("hostPath.path") - if paths != nil && strings.Contains(fmt.Sprintf("%v", paths), "/proc") { - found++ + paths, ok := data.([]interface{}) + if ok && paths != nil { + if strings.Contains(fmt.Sprintf("%v", paths), "/proc") { + found++ + } } return found diff --git a/pkg/rules/procMount_test.go b/pkg/rules/procMount_test.go index eff9237fd..96423a10b 100644 --- a/pkg/rules/procMount_test.go +++ b/pkg/rules/procMount_test.go @@ -3,7 +3,7 @@ package rules import ( "testing" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) func Test_ProcMount_Pod(t *testing.T) { diff --git a/pkg/rules/readOnlyRootFilesystem_test.go b/pkg/rules/readOnlyRootFilesystem_test.go index a29b66195..f73120c1a 100644 --- a/pkg/rules/readOnlyRootFilesystem_test.go +++ b/pkg/rules/readOnlyRootFilesystem_test.go @@ -3,7 +3,7 @@ package rules import ( "testing" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) func Test_ReadOnlyRootFilesystem(t *testing.T) { diff --git a/pkg/rules/requestsCPU.go b/pkg/rules/requestsCPU.go index 597dd8a62..153d0ad1a 100644 --- a/pkg/rules/requestsCPU.go +++ b/pkg/rules/requestsCPU.go @@ -2,6 +2,7 @@ package rules import ( "bytes" + "github.com/thedevsaddam/gojsonq/v2" ) @@ -9,12 +10,13 @@ func RequestsCPU(json []byte) int { spec := getSpecSelector(json) found := 0 - paths := gojsonq.New().Reader(bytes.NewReader(json)). + data := gojsonq.New().Reader(bytes.NewReader(json)). From(spec + ".containers"). Only("resources.requests.cpu") - if paths != nil { - found += len(paths.([]interface{})) + paths, ok := data.([]interface{}) + if ok && paths != nil { + found += len(paths) } return found diff --git a/pkg/rules/requestsCPU_test.go b/pkg/rules/requestsCPU_test.go index 60aa21cb3..11b61d115 100644 --- a/pkg/rules/requestsCPU_test.go +++ b/pkg/rules/requestsCPU_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_RequestsCPU_Pod(t *testing.T) { diff --git a/pkg/rules/requestsMemory.go b/pkg/rules/requestsMemory.go index e3ae9cc8a..790aa28ce 100644 --- a/pkg/rules/requestsMemory.go +++ b/pkg/rules/requestsMemory.go @@ -2,6 +2,7 @@ package rules import ( "bytes" + "github.com/thedevsaddam/gojsonq/v2" ) @@ -9,12 +10,13 @@ func RequestsMemory(json []byte) int { spec := getSpecSelector(json) found := 0 - paths := gojsonq.New().Reader(bytes.NewReader(json)). + data := gojsonq.New().Reader(bytes.NewReader(json)). From(spec + ".containers"). Only("resources.requests.memory") - if paths != nil { - found += len(paths.([]interface{})) + paths, ok := data.([]interface{}) + if ok && paths != nil { + found += len(paths) } return found diff --git a/pkg/rules/requestsMemory_test.go b/pkg/rules/requestsMemory_test.go index e3c7e590c..06ab27a35 100644 --- a/pkg/rules/requestsMemory_test.go +++ b/pkg/rules/requestsMemory_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_RequestsMemory_Pod(t *testing.T) { diff --git a/pkg/rules/runAsGroup_test.go b/pkg/rules/runAsGroup_test.go index de6c554b0..047e2f567 100644 --- a/pkg/rules/runAsGroup_test.go +++ b/pkg/rules/runAsGroup_test.go @@ -3,7 +3,7 @@ package rules import ( "testing" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) func Test_RunAsGroup_Pod(t *testing.T) { diff --git a/pkg/rules/runAsNonRoot_test.go b/pkg/rules/runAsNonRoot_test.go index b1cdc83b0..7e0ef6a29 100644 --- a/pkg/rules/runAsNonRoot_test.go +++ b/pkg/rules/runAsNonRoot_test.go @@ -3,7 +3,7 @@ package rules import ( "testing" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) func Test_RunAsNonRoot_Pod(t *testing.T) { diff --git a/pkg/rules/runAsUser_test.go b/pkg/rules/runAsUser_test.go index 1c5e2dd9a..cbdfa3fa7 100644 --- a/pkg/rules/runAsUser_test.go +++ b/pkg/rules/runAsUser_test.go @@ -3,7 +3,7 @@ package rules import ( "testing" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) func Test_RunAsUser_Pod(t *testing.T) { diff --git a/pkg/rules/seccompAny_test.go b/pkg/rules/seccompAny_test.go index eb08ba29d..282f8da61 100644 --- a/pkg/rules/seccompAny_test.go +++ b/pkg/rules/seccompAny_test.go @@ -3,8 +3,8 @@ package rules import ( "testing" - "github.com/ghodss/yaml" "github.com/thedevsaddam/gojsonq/v2" + "sigs.k8s.io/yaml" ) func Test_isSeccompUnconfined(t *testing.T) { diff --git a/pkg/rules/seccompUnconfined_test.go b/pkg/rules/seccompUnconfined_test.go index 88ad7c1aa..78ce7f931 100644 --- a/pkg/rules/seccompUnconfined_test.go +++ b/pkg/rules/seccompUnconfined_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_SeccompUnconfined(t *testing.T) { diff --git a/pkg/rules/serviceAccountName_test.go b/pkg/rules/serviceAccountName_test.go index e6c36ff02..2586d259d 100644 --- a/pkg/rules/serviceAccountName_test.go +++ b/pkg/rules/serviceAccountName_test.go @@ -1,8 +1,9 @@ package rules import ( - "github.com/ghodss/yaml" "testing" + + "sigs.k8s.io/yaml" ) func Test_ServiceAccountName(t *testing.T) { @@ -38,7 +39,7 @@ kind: Pod metadata: name: my-pod spec: - serviceAccountName: + serviceAccountName: automountServiceAccountToken: false ` diff --git a/pkg/rules/volumeClaimAccessModeReadWriteOnce.go b/pkg/rules/volumeClaimAccessModeReadWriteOnce.go index 0ba52f73b..ab1bc7623 100644 --- a/pkg/rules/volumeClaimAccessModeReadWriteOnce.go +++ b/pkg/rules/volumeClaimAccessModeReadWriteOnce.go @@ -25,12 +25,15 @@ func VolumeClaimAccessModeReadWriteOnce(json []byte) int { found := 0 - paths := gojsonq.New().Reader(bytes.NewReader(json)). + data := gojsonq.New().Reader(bytes.NewReader(json)). From("spec.volumeClaimTemplates"). Only("spec.accessModes") - if paths != nil && strings.Contains(fmt.Sprintf("%v", paths), "accessModes:[ReadWriteOnce]") { - found++ + paths, ok := data.([]interface{}) + if ok && paths != nil { + if strings.Contains(fmt.Sprintf("%v", paths), "accessModes:[ReadWriteOnce]") { + found++ + } } return found diff --git a/pkg/rules/volumeClaimAccessModeReadWriteOnce_test.go b/pkg/rules/volumeClaimAccessModeReadWriteOnce_test.go index 152a1bb0c..a85f05b4f 100644 --- a/pkg/rules/volumeClaimAccessModeReadWriteOnce_test.go +++ b/pkg/rules/volumeClaimAccessModeReadWriteOnce_test.go @@ -3,7 +3,7 @@ package rules import ( "testing" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) func Test_VolumeClaimAccessModeReadWriteOnce(t *testing.T) { diff --git a/pkg/rules/volumeClaimRequestsStorage_test.go b/pkg/rules/volumeClaimRequestsStorage_test.go index 0990e1617..7b161d4e0 100644 --- a/pkg/rules/volumeClaimRequestsStorage_test.go +++ b/pkg/rules/volumeClaimRequestsStorage_test.go @@ -3,7 +3,7 @@ package rules import ( "testing" - "github.com/ghodss/yaml" + "sigs.k8s.io/yaml" ) func Test_VolumeClaimVolumeClaimRequestsStorage(t *testing.T) { diff --git a/pkg/server/server.go b/pkg/server/server.go index fc66b7465..69e531872 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -18,6 +18,124 @@ import ( "go.uber.org/zap" ) +type HandlerFunc func(c context.Context) error + +type HTTPError interface { + error + Unwrap() error + Msg() string + Status() int +} + +type httpError struct { + err error + statusCode int + msg string // public message to provide to users +} + +var ( + // check at compile time that httpError implements HTTPError correctly + _ HTTPError = &httpError{} +) + +func (he *httpError) Error() string { return he.err.Error() } + +func (he *httpError) Unwrap() error { return he.err } + +func (he *httpError) Wrap(err error) *httpError { + if he == nil { + return nil + } + he.err = errors.Join(he.err, err) + return he +} + +func (he *httpError) Msg() string { + // if a public message is set + if he.msg != "" { + return he.msg + } + + // fallback generic responses based on status + switch he.statusCode { + case http.StatusBadRequest: + return "Bad Request" + case http.StatusUnauthorized: + return "Unauthorized" + case http.StatusForbidden: + return "Forbidden" + case http.StatusNotFound: + return "Not Found" + default: + return "Internal Server Error" + } +} +func (he *httpError) Status() int { return he.statusCode } + +// In wrapping error treat all httpError types as nil-able +func WrapHTTPError(err error) *httpError { + if err == nil { + return nil + } + return &httpError{ + statusCode: http.StatusBadRequest, + err: err, + } +} + +// In wrapping error treat all httpError types as nil-able +func NewHTTPError(msg string) *httpError { + return &httpError{ + statusCode: http.StatusBadRequest, + msg: msg, + err: errors.New(msg), + } +} + +func (he *httpError) WithStatus(status int) *httpError { + if he == nil { + return nil + } + he.statusCode = status + return he +} + +func (he *httpError) WithMsg(msg string) *httpError { + if he == nil { + return nil + } + he.msg = msg + return he +} + +func (he *httpError) Public() *httpError { + if he == nil { + return nil + } + he.msg = he.err.Error() + return he +} + +func (he *httpError) Private() *httpError { + if he == nil { + return nil + } + he.msg = "" + return he +} + +// errorHandler allows for writing a handler which can return an error then this +// function can handle it generically conforming to http.HandlerFunc. +func errorHandler(logger *zap.SugaredLogger, f func(http.ResponseWriter, *http.Request) HTTPError) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + httpError := f(w, r) + if httpError != nil { + logger.Errorf("%s %+v", httpError.Msg(), httpError.Error()) + http.Error(w, httpError.Msg(), httpError.Status()) + } + } +} + // ListenAndServe starts a web server and waits for SIGTERM func ListenAndServe( addr string, @@ -32,10 +150,11 @@ func ListenAndServe( mux.Handle("/", scanHandler(logger, keypath, schemaConfig)) mux.Handle("/scan", scanHandler(logger, keypath, schemaConfig)) mux.Handle("/metrics", promhttp.Handler()) - mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/healthz", errorHandler(logger, func(w http.ResponseWriter, r *http.Request) HTTPError { w.WriteHeader(http.StatusOK) - w.Write([]byte("OK\n")) - }) + _, err := w.Write([]byte("OK\n")) + return WrapHTTPError(err) + })) srv := &http.Server{ Addr: addr, @@ -95,45 +214,45 @@ func retrieveRequestData(r *http.Request) ([]byte, error) { body, err := io.ReadAll(r.Body) if err != nil { - return nil, errors.New("Error reading request body") + return nil, errors.New("failed reading request body") } - defer r.Body.Close() if string(body[:formPrefixLen]) == formPrefix { body = body[formPrefixLen:] } + err = r.Body.Close() + if err != nil { + return nil, err + } + return body, nil } func scanHandler(logger *zap.SugaredLogger, keypath string, schemaConfig ruler.SchemaConfig) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + return errorHandler(logger, func(w http.ResponseWriter, r *http.Request) HTTPError { if r.Method == http.MethodGet { http.Redirect(w, r, "https://kubesec.io", http.StatusSeeOther) - return + return nil } // fail early if no in-toto signing key is configured for this server if r.URL.Query().Get("in-toto") != "" && keypath == "" { logger.Errorf("Attempted to serve an in-toto payload but no key is available") w.WriteHeader(http.StatusInternalServerError) - return + return NewHTTPError("attempted to serve an in-toto payload but no key is available") } const fileName = "API" body, err := retrieveRequestData(r) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error() + "\n")) - return + return WrapHTTPError(err).WithStatus(http.StatusBadRequest) } var payload interface{} reports, err := ruler.NewRuleset(logger).Run(fileName, body, schemaConfig) if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error() + "\n")) - return + return WrapHTTPError(err).WithStatus(http.StatusBadRequest).Public() // pass through report error } if r.URL.Query().Get("in-toto") != "" { @@ -141,17 +260,13 @@ func scanHandler(logger *zap.SugaredLogger, keypath string, schemaConfig ruler.S err := intotoKey.LoadKey(keypath, "ed25519", []string{"sha256", "sha512"}) if err != nil { - logger.Errorf("Attempted to serve an in-toto payload but the key is unavailable: %v", - err.Error()) - w.WriteHeader(http.StatusInternalServerError) - return + return NewHTTPError("attempted to serve an in-toto payload but the key is unavailable").Wrap(err) } link := ruler.GenerateInTotoLink(reports, body) err = link.Sign(intotoKey) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return + return NewHTTPError("could not sign in-toto link").Wrap(err) } payload = map[string]interface{}{ "reports": reports, @@ -163,8 +278,7 @@ func scanHandler(logger *zap.SugaredLogger, keypath string, schemaConfig ruler.S res, err := json.Marshal(payload) if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return + return NewHTTPError("failed to marshal JSON").Wrap(err) } w.Header().Set("Content-Type", "application/json") @@ -172,8 +286,12 @@ func scanHandler(logger *zap.SugaredLogger, keypath string, schemaConfig ruler.S formattedOutput, err := report.PrettyJSON(res) if err != nil { w.WriteHeader(http.StatusInternalServerError) - return + return NewHTTPError("failed to pretty format the JSON report").Wrap(err) + } + _, err = w.Write([]byte(string(formattedOutput) + "\n")) + if err != nil { + return WrapHTTPError(err) } - w.Write([]byte(string(formattedOutput) + "\n")) + return nil }) } diff --git a/pkg/util/util.go b/pkg/util/util.go index 0d19fa891..06b6b7337 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -9,7 +9,7 @@ import ( "strings" "text/tabwriter" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) // NewTabWriter returns a default writer to write tables. @@ -29,7 +29,14 @@ func Print(format string, in interface{}, w io.Writer, fn PrintTable) error { switch format { case "yaml": - out, err = yaml.Marshal(in) + // yaml.Marshal() was producing yaml with 4 space indentation + enc := yaml.NewEncoder(w) + enc.SetIndent(2) + err := enc.Encode(in) + if err != nil { + return err + } + err = enc.Close() if err != nil { return err } @@ -40,11 +47,11 @@ func Print(format string, in interface{}, w io.Writer, fn PrintTable) error { } case "table": if fn == nil { - return fmt.Errorf("Print table function can not be nil") + return fmt.Errorf("print table function can not be nil") } return fn(w) default: - return fmt.Errorf("Unkown printing format: %s", format) + return fmt.Errorf("unknown printing format: %s", format) } _, err = fmt.Fprint(w, string(out)) diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index f26306b69..938e265c0 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -45,8 +45,8 @@ func TestPrint(t *testing.T) { printTableFn: nil, want: `name: test values: -- val1 -- val2 + - val1 + - val2 `, wantErr: false, }, @@ -62,10 +62,16 @@ values: format: "table", printTableFn: func(w io.Writer) error { tw := NewTabWriter(w) - fmt.Fprintf(tw, "ID\tValues\n") - fmt.Fprintf(tw, "%s\t%s\n", + _, err := fmt.Fprintf(tw, "ID\tValues\n") + if err != nil { + t.Error(err) + } + _, err = fmt.Fprintf(tw, "%s\t%s\n", TestObject.Name, strings.Join(TestObject.Values, ",")) + if err != nil { + t.Error(err) + } return tw.Flush() }, want: `ID |Values @@ -81,7 +87,6 @@ test |val1,val2 } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { var b bytes.Buffer diff --git a/test/3_todo.bats b/test/3_todo.bats index 65a236db4..f8e41d5e8 100644 --- a/test/3_todo.bats +++ b/test/3_todo.bats @@ -19,7 +19,7 @@ teardown() { # TODO(ajm) BEHAVIOURAL CHANGE (previous scan didn't account for all containers) - FIX BEFORE RELEASE @test "passes production dump" { - run _app ${TEST_DIR}/asset/score-1-prod-dump.yaml + run _app "${TEST_DIR}/asset/score-1-prod-dump.yaml" assert_lt_zero_points }