From 77f8980252e32e14a67b636649008797c76345c3 Mon Sep 17 00:00:00 2001 From: Matthias Bertschy Date: Mon, 24 Jun 2024 17:39:50 +0200 Subject: [PATCH] add matchLabel parsing for other netpol Signed-off-by: Matthias Bertschy --- .github/workflows/pr.yaml | 2 +- .github/workflows/release.yaml | 5 +- armometadata/k8sutils.go | 103 ++++++++---------- armometadata/k8sutils_test.go | 61 +++++++++++ .../testdata/caliconetworkpolicy.json | 31 ++++++ .../testdata/ciliumnetworkpolicy.json | 61 +++++++++++ armometadata/testdata/istionetworkpolicy.json | 32 ++++++ go.mod | 4 +- go.sum | 12 +- 9 files changed, 245 insertions(+), 66 deletions(-) create mode 100644 armometadata/testdata/caliconetworkpolicy.json create mode 100644 armometadata/testdata/ciliumnetworkpolicy.json create mode 100644 armometadata/testdata/istionetworkpolicy.json diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 6b4e46e..df49c32 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -18,5 +18,5 @@ jobs: security-events: write uses: kubescape/workflows/.github/workflows/go-basic-tests.yaml@main with: - GO_VERSION: 1.19 + GO_VERSION: 1.22 secrets: inherit diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ba19ec3..5c11eb7 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,7 +1,7 @@ name: Release-Tag on: push: - branches: [ master, main ] + branches: [ master, main ] paths-ignore: - '**.md' ### Ignore running when .md files change - '**.yaml' ### Ignore running when .yaml files change @@ -17,7 +17,7 @@ jobs: security-events: write uses: kubescape/workflows/.github/workflows/go-basic-tests.yaml@main with: - GO_VERSION: 1.19 + GO_VERSION: 1.22 secrets: inherit release: needs: test @@ -29,4 +29,3 @@ jobs: - uses: rickstaa/action-create-tag@v1 with: tag: "v0.0.${{ github.run_number }}" - \ No newline at end of file diff --git a/armometadata/k8sutils.go b/armometadata/k8sutils.go index 56d1c30..1020006 100644 --- a/armometadata/k8sutils.go +++ b/armometadata/k8sutils.go @@ -1,11 +1,11 @@ package armometadata import ( - "bytes" "encoding/json" "fmt" "hash/fnv" "path" + "slices" "strings" "github.com/armosec/utils-k8s-go/wlid" @@ -126,67 +126,60 @@ func ExtractMetadataFromJsonBytes(input []byte) (Metadata, error) { PodSelectorMatchLabels: map[string]string{}, } // ujson parsing - var parent, subParent string + jsonPathElements := make([]string, 0) err := ujson.Walk(input, func(level int, key, value []byte) bool { - switch level { - case 1: - if bytes.EqualFold(key, []byte(`"kind"`)) { - m.Kind = unquote(value) - } - - if bytes.EqualFold(key, []byte(`"apiVersion"`)) { - m.ApiVersion = unquote(value) - } - - // skip everything except metadata and spec - if !bytes.EqualFold(key, []byte(`"metadata"`)) && !bytes.EqualFold(key, []byte(`"spec"`)) { - return false - } - - parent = unquote(key) - case 2: - if parent == "metadata" { - // read creationTimestamp - if bytes.EqualFold(key, []byte(`"creationTimestamp"`)) { - m.CreationTimestamp = unquote(value) - } - // read resourceVersion - if bytes.EqualFold(key, []byte(`"resourceVersion"`)) { - m.ResourceVersion = unquote(value) - } - - } - - // record parent for level 3 - subParent = unquote(key) - - case 3: - // read annotations - if subParent == "annotations" { - m.Annotations[unquote(key)] = unquote(value) - } - // read labels - if subParent == "labels" { - m.Labels[unquote(key)] = unquote(value) - } - - case 4: - // read ownerReferences - if subParent == "ownerReferences" { - m.OwnerReferences[unquote(key)] = unquote(value) - } - - if subParent == "podSelector" { - m.PodSelectorMatchLabels[unquote(key)] = unquote(value) - } - + if level > 0 { + jsonPathElements = slices.Replace(jsonPathElements, level-1, len(jsonPathElements), unquote(key)) + } + jsonPath := strings.Join(jsonPathElements, ".") + switch { + case jsonPath == "kind": + m.Kind = unquote(value) + case jsonPath == "apiVersion": + m.ApiVersion = unquote(value) + case jsonPath == "metadata.creationTimestamp": + m.CreationTimestamp = unquote(value) + case jsonPath == "metadata.resourceVersion": + m.ResourceVersion = unquote(value) + case strings.HasPrefix(jsonPath, "metadata.annotations."): + m.Annotations[unquote(key)] = unquote(value) + case strings.HasPrefix(jsonPath, "metadata.labels."): + m.Labels[unquote(key)] = unquote(value) + case strings.HasPrefix(jsonPath, "metadata.ownerReferences.."): + m.OwnerReferences[unquote(key)] = unquote(value) + case m.ApiVersion == "cilium.io/v2" && strings.HasPrefix(jsonPath, "spec.endpointSelector.matchLabels."): + m.PodSelectorMatchLabels[unquote(key)] = unquote(value) + case m.ApiVersion == "networking.k8s.io/v1" && strings.HasPrefix(jsonPath, "spec.podSelector.matchLabels."): + m.PodSelectorMatchLabels[unquote(key)] = unquote(value) + case m.ApiVersion == "security.istio.io/v1" && strings.HasPrefix(jsonPath, "spec.selector.matchLabels."): + m.PodSelectorMatchLabels[unquote(key)] = unquote(value) + case m.ApiVersion == "projectcalico.org/v3" && jsonPath == "spec.selector": + m.PodSelectorMatchLabels = parseCalicoSelector(value) } - return true }) return m, err } +func parseCalicoSelector(value []byte) map[string]string { + selector := map[string]string{} + for _, rule := range strings.Split(unquote(value), "&&") { + s := strings.Split(rule, "==") + if len(s) != 2 { + continue + } + k := strings.TrimSpace(s[0]) + v := strings.TrimSpace(s[1]) + // strconv.Unquote does not handle single quotes + if (strings.HasPrefix(v, "'") && strings.HasSuffix(v, "'")) || + (strings.HasPrefix(v, "\"") && strings.HasSuffix(v, "\"")) { + v = v[1 : len(v)-1] + } + selector[k] = v + } + return selector +} + func unquote(value []byte) string { buf, err := ujson.Unquote(value) if err != nil { diff --git a/armometadata/k8sutils_test.go b/armometadata/k8sutils_test.go index fda6956..1f80d8d 100644 --- a/armometadata/k8sutils_test.go +++ b/armometadata/k8sutils_test.go @@ -228,6 +228,33 @@ func TestExtractMetadataFromJsonBytes(t *testing.T) { apiVersion: "spdx.softwarecomposition.kubescape.io/v1beta1", podSelectorMatchLabels: map[string]string{}, }, + { + name: "caliconetworkpolicy", + annotations: map[string]string{}, + labels: map[string]string{}, + ownerReferences: map[string]string{}, + kind: "NetworkPolicy", + apiVersion: "projectcalico.org/v3", + podSelectorMatchLabels: map[string]string{"role": "database"}, + }, + { + name: "ciliumnetworkpolicy", + annotations: map[string]string{}, + labels: map[string]string{}, + ownerReferences: map[string]string{}, + kind: "CiliumNetworkPolicy", + apiVersion: "cilium.io/v2", + podSelectorMatchLabels: map[string]string{"app": "frontend"}, + }, + { + name: "istionetworkpolicy", + annotations: map[string]string{}, + labels: map[string]string{}, + ownerReferences: map[string]string{}, + kind: "AuthorizationPolicy", + apiVersion: "security.istio.io/v1", + podSelectorMatchLabels: map[string]string{"app": "myapi"}, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -254,3 +281,37 @@ func BenchmarkExtractMetadataFromJsonBytes(b *testing.B) { _, _ = ExtractMetadataFromJsonBytes(input) } } + +func Test_parseCalicoSelector(t *testing.T) { + tests := []struct { + name string + value []byte + want map[string]string + }{ + { + name: "empty", + value: []byte(""), + want: map[string]string{}, + }, + { + name: "single", + value: []byte(`"role == 'database'"`), + want: map[string]string{"role": "database"}, + }, + { + name: "multiple", + value: []byte(`"role == 'database' && tier == 'frontend'"`), + want: map[string]string{"role": "database", "tier": "frontend"}, + }, + { + name: "real", + value: []byte(`"app.kubernetes.io/instance == 'kubescape' && app.kubernetes.io/name == 'operator' && tier == 'ks-control-plane'"`), + want: map[string]string{"app.kubernetes.io/instance": "kubescape", "app.kubernetes.io/name": "operator", "tier": "ks-control-plane"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, parseCalicoSelector(tt.value), "parseCalicoSelector(%v)", tt.value) + }) + } +} diff --git a/armometadata/testdata/caliconetworkpolicy.json b/armometadata/testdata/caliconetworkpolicy.json new file mode 100644 index 0000000..bb3e948 --- /dev/null +++ b/armometadata/testdata/caliconetworkpolicy.json @@ -0,0 +1,31 @@ +{ + "apiVersion": "projectcalico.org/v3", + "kind": "NetworkPolicy", + "metadata": { + "name": "allow-tcp-6379", + "namespace": "production" + }, + "spec": { + "selector": "role == 'database'", + "types": [ + "Ingress", + "Egress" + ], + "ingress": [ + { + "action": "Log", + "protocol": "TCP", + "source": { + "selector": "role == 'frontend'" + } + }, + { + "action": "Deny", + "protocol": "TCP", + "source": { + "selector": "role == 'frontend'" + } + } + ] + } +} diff --git a/armometadata/testdata/ciliumnetworkpolicy.json b/armometadata/testdata/ciliumnetworkpolicy.json new file mode 100644 index 0000000..2f55aea --- /dev/null +++ b/armometadata/testdata/ciliumnetworkpolicy.json @@ -0,0 +1,61 @@ +{ + "apiVersion": "cilium.io/v2", + "kind": "CiliumNetworkPolicy", + "metadata": { + "name": "untitled-policy" + }, + "spec": { + "endpointSelector": { + "matchLabels": { + "app": "frontend" + } + }, + "egress": [ + { + "toEndpoints": [ + { + "matchLabels": { + "io.kubernetes.pod.namespace": "kube-system", + "k8s-app": "kube-dns" + } + } + ], + "toPorts": [ + { + "ports": [ + { + "port": "53", + "protocol": "UDP" + } + ], + "rules": { + "dns": [ + { + "matchPattern": "*" + } + ] + } + } + ] + }, + { + "toEndpoints": [ + { + "matchLabels": { + "app": "backend" + } + } + ], + "toPorts": [ + { + "ports": [ + { + "port": "443" + } + ] + } + ] + } + ] + } +} diff --git a/armometadata/testdata/istionetworkpolicy.json b/armometadata/testdata/istionetworkpolicy.json new file mode 100644 index 0000000..1abbb85 --- /dev/null +++ b/armometadata/testdata/istionetworkpolicy.json @@ -0,0 +1,32 @@ +{ + "apiVersion": "security.istio.io/v1", + "kind": "AuthorizationPolicy", + "metadata": { + "namespace": "ns1", + "name": "anyname" + }, + "spec": { + "selector": { + "matchLabels": { + "app": "myapi" + } + }, + "action": "AUDIT", + "rules": [ + { + "to": [ + { + "operation": { + "methods": [ + "GET" + ], + "paths": [ + "/user/profile/*" + ] + } + } + ] + } + ] + } +} diff --git a/go.mod b/go.mod index 04873a2..d04b253 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/armosec/utils-k8s-go -go 1.18 +go 1.22.4 require ( github.com/armosec/armoapi-go v0.0.234 @@ -20,8 +20,6 @@ require ( require ( github.com/armosec/gojay v1.2.15 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/docker/go-connections v0.4.0 // indirect - github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/go-logr/logr v1.2.4 // indirect diff --git a/go.sum b/go.sum index f586df0..1f0ae5d 100644 --- a/go.sum +++ b/go.sum @@ -125,10 +125,6 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/docker/docker v25.0.1+incompatible h1:k5TYd5rIVQRSqcTwCID+cyVA0yRg86+Pcrz1ls0/frA= github.com/docker/docker v25.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= @@ -189,6 +185,7 @@ github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -268,6 +265,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -342,6 +340,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -399,10 +398,12 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= +github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= @@ -580,6 +581,7 @@ go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -872,6 +874,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1033,6 +1036,7 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= +gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=