[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: weekly\n  - package-ecosystem: gomod\n    directory: /\n    schedule:\n      interval: weekly\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: release\n\non:\n  push:\n    tags:\n      - 'v*'\n\npermissions:\n  contents: write\n  packages: write\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    steps:\n      - name: checkout\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: fetch tags\n        run: git fetch --force --tags\n\n      - name: setup go\n        uses: actions/setup-go@v6\n        with:\n          go-version-file: 'go.mod'\n          cache: true\n\n      - name: release\n        id: goreleaser\n        uses: goreleaser/goreleaser-action@v7\n        with:\n          distribution: goreleaser\n          version: latest\n          args: release --clean\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: krew\n        uses: rajatjindal/krew-release-bot@v0.0.50\n\n      - name: setup qemu\n        uses: docker/setup-qemu-action@v3\n\n      - name: setup docker buildx\n        uses: docker/setup-buildx-action@v3\n        \n      - name: login\n        uses: docker/login-action@v3\n        with:\n          registry: ghcr.io\n          username: ${{ github.actor }}\n          password: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: metadata\n        id: meta\n        uses: docker/metadata-action@v5\n        with:\n          images: ghcr.io/${{ github.repository }}\n          tags: |\n            type=semver,prefix=v,pattern={{version}}\n            type=semver,prefix=v,pattern={{major}}.{{minor}}\n\n      - name: create dockerfile.cross\n        run: |\n          sed -e '1 s/\\(^FROM\\)/FROM --platform=\\$\\{BUILDPLATFORM\\}/; t' -e ' 1,// s//FROM --platform=\\$\\{BUILDPLATFORM\\}/' Dockerfile > Dockerfile.cross\n          cat Dockerfile.cross\n\n      - name: build and push\n        uses: docker/build-push-action@v6\n        with:\n          build-args: |\n            VERSION=${{ fromJSON(steps.goreleaser.outputs.metadata).version }}\n            COMMIT=${{ fromJSON(steps.goreleaser.outputs.metadata).commit }}\n            DATE=${{ fromJSON(steps.goreleaser.outputs.metadata).date }}\n          context: .\n          platforms: linux/arm64,linux/amd64,linux/s390x,linux/ppc64le\n          file: Dockerfile.cross\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: test\non:\n  pull_request:\n    branches: [main]\n  push:\n    branches: [main]\njobs:\n  validate:\n    runs-on: ubuntu-latest\n    steps:\n      - name: checkout\n        uses: actions/checkout@v6\n\n      - name: setup go\n        uses: actions/setup-go@v6\n        with:\n          go-version-file: 'go.mod'\n          cache: true\n\n      - name: test\n        run: make test\n\n      - name: build\n        run: make build\n\n      - name: check license headers\n        run: make checklicense\n"
  },
  {
    "path": ".gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\nbin\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.idea\n\ndist/\n"
  },
  {
    "path": ".goreleaser.yaml",
    "content": "version: 2\nbefore:\n  hooks:\n    - go mod tidy\nbuilds:\n  - env:\n      - CGO_ENABLED=0\n    goos:\n      - linux\n      - windows\n      - darwin\n    ldflags:\n      - >-\n          -s -w\n          -X github.com/undistro/marvin/pkg/version.version={{.Version}}\n          -X github.com/undistro/marvin/pkg/version.commit={{.Commit}} \n          -X github.com/undistro/marvin/pkg/version.date={{.Date}} \n\narchives:\n  - formats: [tar.gz]\n    name_template: >-\n      {{ .ProjectName }}_\n      {{- title .Os }}_\n      {{- if eq .Arch \"amd64\" }}x86_64\n      {{- else if eq .Arch \"386\" }}i386\n      {{- else }}{{ .Arch }}{{ end }}\n      {{- if .Arm }}v{{ .Arm }}{{ end }}\n    format_overrides:\n    - goos: windows\n      formats: [zip]\nchecksum:\n  name_template: 'checksums.txt'\nsnapshot:\n  version_template: \"{{ incpatch .Version }}-next\"\nchangelog:\n  sort: asc\n  filters:\n    exclude:\n      - '^docs:'\n      - '^test:'\nrelease:\n  prerelease: auto\n"
  },
  {
    "path": ".krew.yaml",
    "content": "apiVersion: krew.googlecontainertools.github.com/v1alpha2\nkind: Plugin\nmetadata:\n  name: marvin\nspec:\n  homepage: https://github.com/undistro/marvin\n  shortDescription: Scan clusters with your own checks written in CEL.\n  description: |\n    Marvin scans a Kubernetes cluster by performing CEL expressions \n    to report potential issues, misconfigurations and vulnerabilities.\n    \n    Marvin allows you to write your own checks by using CEL expressions.\n  version: {{ .TagName }}\n  platforms:\n  - selector:\n      matchLabels:\n        os: linux\n        arch: amd64\n    {{addURIAndSha \"https://github.com/undistro/marvin/releases/download/{{ .TagName }}/marvin_Linux_x86_64.tar.gz\" .TagName }}\n    bin: marvin\n  - selector:\n      matchLabels:\n        os: linux\n        arch: arm64\n    {{addURIAndSha \"https://github.com/undistro/marvin/releases/download/{{ .TagName }}/marvin_Linux_arm64.tar.gz\" .TagName }}\n    bin: marvin\n  - selector:\n      matchLabels:\n        os: darwin\n        arch: amd64\n    {{addURIAndSha \"https://github.com/undistro/marvin/releases/download/{{ .TagName }}/marvin_Darwin_x86_64.tar.gz\" .TagName }}\n    bin: marvin\n  - selector:\n      matchLabels:\n        os: darwin\n        arch: arm64\n    {{addURIAndSha \"https://github.com/undistro/marvin/releases/download/{{ .TagName }}/marvin_Darwin_arm64.tar.gz\" .TagName }}\n    bin: marvin\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\nundistro@getup.io.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations."
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Marvin\n\n:+1::tada: Thanks for taking the time to contribute! :tada::+1:\n\nThe following is a set of guidelines for contributing to Marvin, \nwhich are hosted in the [Undistro Organization](https://github.com/undistro) on GitHub. \n\n## How Can I Contribute?\n\n- **Giving us a star.** It may not seem like much, but it really makes a\n  difference. This is something that everyone can do to help out Marvin.\n  GitHub stars help the project gain visibility and stand out.\n\n- **Reviewing the documentation.** \n  Most documentation just needs a review for proper spelling and grammar. \n  If you think a document can be improved in any way, \n  feel free to open a Pull Request with your suggestions.\n\n- **Reporting bugs.** We use GitHub issues to track public bugs. \n  Report a bug by [opening a new issue](https://github.com/undistro/marvin/issues/new/choose).\n  \n- **Pull Request.** Unless you are fixing a known bug, we **strongly** \n  recommend discussing it with the core team via a GitHub issue before \n  getting started to ensure your work is consistent with Marvin's roadmap and architecture.\n\n"
  },
  {
    "path": "Dockerfile",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nFROM golang:1.25.4-alpine AS builder\nARG TARGETOS\nARG TARGETARCH\nARG VERSION\nARG COMMIT\nARG DATE\n\nWORKDIR /workspace\nCOPY go.mod go.mod\nCOPY go.sum go.sum\nRUN go mod download\n\nCOPY main.go main.go\nCOPY cmd/ cmd/\nCOPY internal/ internal/\nCOPY pkg/ pkg/\n\nRUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build \\\n    -ldflags=\"-s -w -X github.com/undistro/marvin/pkg/version.version=${VERSION:-docker} \\\n    -X github.com/undistro/marvin/pkg/version.commit=${COMMIT} \\\n    -X github.com/undistro/marvin/pkg/version.date=${DATE}\" -a -o marvin main.go\n\nFROM alpine:3.22\nRUN apk update && apk upgrade && rm -rf /var/cache/apk\nRUN addgroup -g 8494 -S nonroot && adduser -u 8494 -D -S nonroot -G nonroot\nUSER 8494:8494\n\nWORKDIR /\nCOPY --from=builder /workspace/marvin .\n\nENTRYPOINT [\"/marvin\"]\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.\n"
  },
  {
    "path": "MAINTAINERS.md",
    "content": "# Maintainers\n\nMaintainers are approvers who have shown good technical judgement in guiding feature design & development, have \ndisplayed overall knowledge of the project and features in the project, and are nurturing and receptive to everyone in \nthe community.\n\nThe following table lists the Marvin project core maintainers:\n\n| Name                                                              | GitHub ID                                  | Affiliation                |\n|-------------------------------------------------------------------|--------------------------------------------|----------------------------|\n| [Matheus Moraes](https://www.linkedin.com/in/matheusfm/)          | [@matheusfm](https://github.com/matheusfm) | [Getup](https://getup.io/) |\n| [Kevin Conner](https://www.linkedin.com/in/kevin-conner-26b5554/) | [@knrc](https://github.com/knrc)           | [Getup](https://getup.io/) |\n"
  },
  {
    "path": "Makefile",
    "content": "# Image URL to use all building/pushing image targets\nTAG ?= latest\nIMG ?= ghcr.io/undistro/marvin:${TAG}\n\n# Setting SHELL to bash allows bash commands to be executed by recipes.\n# Options are set to exit when a recipe line exits non-zero or a piped command fails.\nSHELL = /usr/bin/env bash -o pipefail\n.SHELLFLAGS = -ec\n\n.PHONY: all\nall: build\n\n##@ General\n\n.PHONY: help\nhelp: ## Display this help.\n\t@awk 'BEGIN {FS = \":.*##\"; printf \"\\nUsage:\\n  make \\033[36m<target>\\033[0m\\n\"} /^[a-zA-Z_0-9-]+:.*?##/ { printf \"  \\033[36m%-15s\\033[0m %s\\n\", $$1, $$2 } /^##@/ { printf \"\\n\\033[1m%s\\033[0m\\n\", substr($$0, 5) } ' $(MAKEFILE_LIST)\n\n##@ Development\n\n.PHONY: fmt\nfmt: ## Run go fmt against code.\n\tgo fmt ./...\n\n.PHONY: vet\nvet: ## Run go vet against code.\n\tgo vet ./...\n\n.PHONY: test\ntest: fmt vet ## Run tests.\n\tgo test ./... -coverprofile cover.out\n\n.PHONY: addlicense\naddlicense: ## Add copyright license headers in source code files.\n\t@test -s $(LOCALBIN)/addlicense || GOBIN=$(LOCALBIN) go install github.com/google/addlicense@latest\n\t$(LOCALBIN)/addlicense -c \"Undistro Authors\" -l \"apache\" -ignore \".github/**\" -ignore \".idea/**\" -ignore \"dist/**\" -ignore \".goreleaser.yaml\" -ignore \".krew.yaml\" .\n\n.PHONY: checklicense\nchecklicense: ## Check copyright license headers in source code files.\n\t@test -s $(LOCALBIN)/addlicense || GOBIN=$(LOCALBIN) go install github.com/google/addlicense@latest\n\t$(LOCALBIN)/addlicense -c \"Undistro Authors\" -l \"apache\" -ignore \".github/**\" -ignore \".idea/**\" -ignore \"dist/**\" -ignore \".goreleaser.yaml\" -ignore \".krew.yaml\" -check .\n\n##@ Build\n\n.PHONY: build\nbuild: fmt vet ## Build marvin binary.\n\tgo build -ldflags=\"-s -w -X github.com/undistro/marvin/pkg/version.version=${TAG}\" -o bin/marvin main.go\n\n.PHONY: run\nrun: fmt vet ## Run marvin from your host.\n\tgo run ./main.go\n\nPLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le\n.PHONY: docker-buildx\ndocker-buildx: test ## Build and push docker image for cross-platform support.\n\tsed -e '1 s/\\(^FROM\\)/FROM --platform=\\$$\\{BUILDPLATFORM\\}/; t' -e ' 1,// s//FROM --platform=\\$$\\{BUILDPLATFORM\\}/' Dockerfile > Dockerfile.cross\n\t- docker buildx create --name cross-builder\n\tdocker buildx use cross-builder\n\t- docker buildx build --push --platform=$(PLATFORMS) --build-arg VERSION=${TAG} --tag ${IMG} -f Dockerfile.cross .\n\t- docker buildx rm cross-builder\n\trm Dockerfile.cross\n\n.PHONY: docker-build\ndocker-build: test ## Build docker image.\n\tdocker build --build-arg VERSION=${TAG} -t ${IMG} .\n\n.PHONY: docker-push\ndocker-push: ## Push docker image.\n\tdocker push ${IMG}\n\n## Location to install dependencies to\nLOCALBIN ?= $(shell pwd)/bin\n$(LOCALBIN):\n\tmkdir -p $(LOCALBIN)\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n\n<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"assets/banner-dark.png\">\n  <img alt=\"Marvin logo\" src=\"assets/banner-light.png\">\n</picture>\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/undistro/marvin.svg)](https://pkg.go.dev/github.com/undistro/marvin)\n[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/marvin)](https://artifacthub.io/packages/krew/krew-index/marvin)\n[![Test](https://github.com/undistro/marvin/actions/workflows/test.yml/badge.svg?branch=main&event=push)](https://github.com/undistro/marvin/actions/workflows/test.yml)\n![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/undistro/marvin?sort=semver&color=brightgreen)\n![GitHub](https://img.shields.io/github/license/undistro/marvin?color=brightgreen)\n[![Go Report Card](https://goreportcard.com/badge/github.com/undistro/marvin)](https://goreportcard.com/report/github.com/undistro/marvin)\n![GitHub all releases](https://img.shields.io/github/downloads/undistro/marvin/total)\n\n</div>\n\nMarvin is a CLI tool designed to help Kubernetes cluster administrators \nensure the security and reliability of their environments. \n\nUsing a comprehensive set of [CEL (Common Expression Language)](https://github.com/google/cel-spec) expressions, \nMarvin performs extensive checks on cluster resources, \nidentifying potential issues, misconfigurations, and vulnerabilities that could pose a risk to the system. \nIt helps ensure that your Kubernetes clusters are always in compliance with best practices and industry standards.\n\nMarvin is also used as a plugin in [Zora](https://zora-docs.undistro.io/latest/).\n\n<!-- TOC -->\n* [Installation](#installation)\n  * [Manually](#manually)\n  * [Install via script](#install-via-script)\n  * [Install via Krew](#install-via-krew)\n  * [Install from source](#install-from-source)\n* [Usage](#usage)\n  * [Built-in checks](#built-in-checks)\n  * [Custom checks](#custom-checks)\n  * [Skipping resources](#skipping-resources)\n  * [RBAC](#rbac)\n* [Contributing](#contributing)\n* [License](#license)\n<!-- TOC -->\n\n_Please [star :star:](https://github.com/undistro/marvin/stargazers) the repo if you want us to continue developing and improving Marvin!_ :grin:\n\n# Installation\n\nThe pre-compiled binaries are available in [GitHub releases page](https://github.com/undistro/marvin/releases) \nand can be installed manually, via script or as a `kubectl` plugin with [Krew](https://krew.sigs.k8s.io).\n\n## Manually\n\n1. Download the file for your system/architecture from the [GitHub releases page](https://github.com/undistro/marvin/releases)\n2. Unpack the downloaded archive (e.g `tar -xzf marvin_Linux_x86_64.tar.gz`)\n3. Make sure the binary has execution bit turned on (`chmod +x ./marvin`)\n4. Move the binary somewhere in your `$PATH` (e.g `sudo mv ./marvin /usr/local/bin/`)\n\n## Install via script\n\nThe process above can be automated by the following script:\n\n```shell\ncurl -sSfL https://raw.githubusercontent.com/undistro/marvin/main/install.sh | sh -s -- -b $HOME/.local/bin\n```\n\n## Install via [Krew](https://krew.sigs.k8s.io)\n\nYou can install Marvin as a `kubectl` plugin via [Krew](https://krew.sigs.k8s.io):\n```shell\nkubectl krew install marvin\n```\n\nThen you can use Marvin with `kubectl` prefix:\n```shell\nkubectl marvin version\n```\n\n## Install from source\n\n```shell\ngo install github.com/undistro/marvin@latest\n```\n\n# Usage\n\n## Built-in checks\n\nScan the current-context Kubernetes cluster performing the [built-in checks](internal/builtins):\n```shell\nmarvin scan\n```\n```\nSEVERITY   ID      CHECK                                                   STATUS   FAILED   PASSED   SKIPPED \nHigh       M-101   Host namespaces                                         Failed   8        25       0         \nHigh       M-104   HostPath volume                                         Failed   8        25       0         \nHigh       M-201   Application credentials stored in configuration files   Failed   2        45       0         \nHigh       M-102   Privileged container                                    Failed   2        31       0         \nHigh       M-103   Insecure capabilities                                   Failed   2        31       0         \nHigh       M-100   Privileged access to the Windows node                   Passed   0        33       0         \nHigh       M-105   Not allowed hostPort                                    Passed   0        33       0         \nMedium     M-113   Container could be running as root user                 Failed   33       0        0         \nMedium     M-407   CPU not limited                                         Failed   31       2        0         \nMedium     M-406   Memory not limited                                      Failed   27       6        0         \nMedium     M-404   Memory requests not specified                           Failed   26       7        0         \nMedium     M-402   Readiness and startup probe not configured              Failed   25       8        0         \nMedium     M-403   Liveness probe not configured                           Failed   25       8        0         \nMedium     M-405   CPU requests not specified                              Failed   23       10       0         \nMedium     M-106   Forbidden AppArmor profile                              Passed   0        33       0         \nMedium     M-107   Forbidden SELinux options                               Passed   0        33       0         \nMedium     M-108   Forbidden proc mount type                               Passed   0        33       0         \nMedium     M-109   Forbidden seccomp profile                               Passed   0        33       0         \nMedium     M-110   Unsafe sysctls                                          Passed   0        33       0         \nMedium     M-112   Allowed privilege escalation                            Passed   0        33       0         \nMedium     M-114   Container running as root UID                           Passed   0        33       0         \nMedium     M-200   Image registry not allowed                              Passed   0        33       0         \nMedium     M-400   Image tagged latest                                     Passed   0        33       0         \nMedium     M-408   Sudo in container entrypoint                            Passed   0        33       0         \nMedium     M-409   Deprecated image registry                               Passed   0        33       0         \nMedium     M-500   Workload in default namespace                           Passed   0        33       0         \nMedium     M-410   Not allowed restartPolicy                               Passed   0        18       0         \nLow        M-116   Not allowed added/dropped capabilities                  Failed   33       0        0         \nLow        M-202   Automounted service account token                       Failed   33       0        0         \nLow        M-115   Not allowed seccomp profile                             Failed   29       4        0         \nLow        M-300   Root filesystem write allowed                           Failed   29       4        0         \nLow        M-111   Not allowed volume type                                 Failed   8        25       0         \nLow        M-203   SSH server running inside container                     Passed   0        39       0         \nLow        M-401   Unmanaged Pod                                           Passed   0        15       0         \n```\n\nThe default output format is `table` which represents a summary of checks result. \nYou can provide `json` or `yaml` in the `-o/--output` flag to get more details.\n\nRun `marvin scan --help` to see all available options.\n\n## Custom checks\n\nMarvin allows you to write your own checks by using [CEL expressions](https://github.com/google/cel-spec) in a YAML file like the example below.\n\n```yaml\nid: CUSTOM-001\nseverity: Medium\nmessage: \"Replicas limit\"\nmatch:\n  resources:\n    - group: apps\n      version: v1\n      resource: deployments\nvalidations:\n  - expression: >\n      object.spec.replicas <= 5\n    message: \"Deployment with more than 5 replicas\"\n```\n\nIf an expression evaluates to `false`, the check fails.\n\nThis is how built-in Marvin checks are defined as well.\nYou can see all the built-in checks in the [`internal/builtins` folder](internal/builtins) for examples.\n\nIf you want to quickly test CEL expressions from your browser, check out the [CEL Playground](https://playcel.undistro.io/).\n\nThen provide the directory path with your custom check files in the `-f/--checks` flag:\n\n```shell\nmarvin scan --disable-builtin --checks ./examples/\n```\n```\nSEVERITY   ID           CHECK            STATUS   FAILED   PASSED   SKIPPED \nMedium     CUSTOM-001   Replicas limit   Passed   0        2        0         \n```\n\nThe flag `--disable-builtin` disables the built-in Marvin checks.\n\nIf the check matches a PodSpec (`Pod`, `ReplicationController`, `ReplicaSet`, `Deployment`, `StatefulSet`, `DaemonSet`, `Job` or `CronJob`)\nthe `podSpec` and `allContainers` inputs are available for expressions.\n\nThe `allContainers` input is a list of all containers including `initContainers` and `ephemeralContainers`.\n\n## Skipping resources\n\nYou can use annotations to skip certain checks for specific resources in your cluster.\nBy adding the `marvin.undistro.io/skip` annotation to a resource, \nyou can specify a comma-separated list of check IDs to skip.\n\nExample:\n```shell\nkubectl annotate deployment nginx marvin.undistro.io/skip='M-202, M-111'\n```\n\nBy default, Marvin will respect the `marvin.undistro.io/skip` annotation when performing checks. \nHowever, you can disable this behavior by using the `--disable-annotation-skip` flag.\nThis flag will cause Marvin to perform all checks on all resources.\n\nIf you prefer to use a different annotation to skip checks, \nyou can use the `--skip-annotation` flag to specify the annotation name. \nExample: `--skip-annotation='my-company.com/skip-checks'`\n\n## RBAC\n\nCurrently, the built-in checks look for the below resources\nand Marvin needs view (`get` and `list`) permission to verify them.\n\n- `v1/pods`\n- `v1/configmaps`\n- `v1/services`\n- `apps/v1/deployments`\n- `apps/v1/daemonsets`\n- `apps/v1/statefulsets`\n- `apps/v1/replicasets`\n- `batch/v1/cronjobs`\n- `batch/v1/jobs`\n\n<details>\n\n<summary> Here is a sample `ClusterRole` for Marvin: </summary>\n\n```yaml\napiVersion: rbac.authorization.k8s.io/v1\nkind: ClusterRole\nmetadata:\n  name: marvin\nrules:\n  - apiGroups: [ \"\" ]\n    resources:\n      - configmaps\n      - pods\n      - services\n    verbs: [ \"get\", \"list\" ]\n  - apiGroups: [ \"apps\" ]\n    resources:\n      - daemonsets\n      - deployments\n      - statefulsets\n      - replicasets\n    verbs: [ \"get\", \"list\" ]\n  - apiGroups: [ batch ]\n    resources:\n      - jobs\n      - cronjobs\n    verbs: [ \"get\", \"list\" ]\n```\n\n</details>\n\n> **Note**\n> You can write a custom check to look at any resource. \n> But Marvin needs view permission. \n> Remember to update RBAC for new resources you want to check.\n\n# Contributing\n\nWe appreciate your contribution.\nPlease refer to our [contributing guideline](https://github.com/undistro/marvin/blob/main/CONTRIBUTING.md) for further information.\nThis project adheres to the Contributor Covenant [code of conduct](https://github.com/undistro/marvin/blob/main/CODE_OF_CONDUCT.md).\n\n# License\n\nMarvin is available under the Apache 2.0 license. See the [LICENSE](LICENSE) file for more info.\n"
  },
  {
    "path": "checks.md",
    "content": "# Checks Overview  \n\nIn the table below, you can view all checks present on Marvin. Click on the #ID column item for more details about each check.\n\n\n| Framework        | #ID                                                                                                  | Severity | Message                                                          |\n|------------------|------------------------------------------------------------------------------------------------------|----------|------------------------------------------------------------------|\n| CIS Benchmarks   | [M-500](/internal/builtins/cis/M-500_default_namespace.yaml)                                         | Medium   | Workloads in default namespace                                   |\n| General          | [M-400](/internal/builtins/general/M-400_image_tag_latest.yaml)                                      | Medium   | Image tagged latest                                              |\n|                  | [M-401](/internal/builtins/general/M-401_unmanaged_pod.yaml)                                         | Low      | Unmanaged Pod                                                    |\n|                  | [M-402](/internal/builtins/general/M-402_readiness_probe.yaml)                                       | Medium   | Readiness and startup probe not configured                       |\n|                  | [M-403](/internal/builtins/general/M-403_liveness_probe.yaml)                                        | Medium   | Liveness probe not configured                                    |\n|                  | [M-404](/internal/builtins/general/M-404_memory_requests.yaml)                                       | Medium   | Memory requests not specified                                    |\n|                  | [M-405](/internal/builtins/general/M-405_cpu_requests.yaml)                                          | Medium   | CPU requests not specified                                       |\n|                  | [M-406](/internal/builtins/general/M-406_memory_limit.yaml)                                          | Medium   | Memory not limited                                               |\n|                  | [M-407](/internal/builtins/general/M-407_cpu_limit.yaml)                                             | Medium   | CPU not limited                                                  |\n|                  | [M-408](/internal/builtins/general/M-408_sudo_container_entrypoint.yaml)                             | Medium   | Sudo in container entrypoint                                     |\n|                  | [M-409](/internal/builtins/general/M-409_deprecated_image_registry.yaml)                             | Medium   | Deprecated image registry                                        |\n|                  | [M-410](/internal/builtins/general/M-410_resource_using_invalid_restartpolicy.yaml)                  | Medium   | Resource is using an invalid restartPolicy                       |\n|                  | [M-411](/internal/builtins/general/M-411_role_binding_referencing_anonymous_or_unauthanticated.yaml) | Medium   | Role Binding referencing anonymous user or unauthenticated group |\n| NSA-CISA         | [M-300](/internal/builtins/nsa/M-300_read_only_root_filesystem.yml)                                  | Low      | Root filesystem write allowed                                    |\n| MITRE ATT&CK     | [M-200](/internal/builtins/mitre/M-200_allowed_registries.yml)                                       | Medium   | Image registry not allowed                                       |\n|                  | [M-201](/internal/builtins/mitre/M-201_app_credentials.yml)                                          | High     | Application credentials stored in configuration files            |\n|                  | [M-202](/internal/builtins/mitre/M-202_auto_mount_service_account.yml)                               | Low      | Automounted service account token                                |\n|                  | [M-203](/internal/builtins/mitre/M-203_ssh.yml)                                                      | Low      | SSH server running inside container                              |\n| PSS - Baseline   | [M-100](/internal/builtins/pss/baseline/M-100_host_process.yml)                                      | High     | Privileged access to the Windows node                            |\n|                  | [M-101](/internal/builtins/pss/baseline/M-101_host_namespaces.yml)                                   | High     | Host namespaces                                                  |\n|                  | [M-102](/internal/builtins/pss/baseline/M-102_privileged_containers.yml)                             | High     | Privileged container                                             |\n|                  | [M-103](/internal/builtins/pss/baseline/M-103_capabilities.yml)                                      | High     | Insecure capabilities                                            |\n|                  | [M-104](/internal/builtins/pss/baseline/M-104_host_path_volumes.yml)                                 | High     | HostPath volume                                                  |\n|                  | [M-105](/internal/builtins/pss/baseline/M-105_host_ports.yml)                                        | High     | Not allowed hostPort                                             |\n|                  | [M-106](/internal/builtins/pss/baseline/M-106_apparmor.yml)                                          | Medium   | Forbidden AppArmor profile                                       |\n|                  | [M-107](/internal/builtins/pss/baseline/M-107_selinux.yml)                                           | Medium   | Forbidden SELinux options                                        |\n|                  | [M-108](/internal/builtins/pss/baseline/M-108_proc_mount.yml)                                        | Medium   | Forbidden proc mount type                                        |\n|                  | [M-109](/internal/builtins/pss/baseline/M-109_seccomp.yml)                                           | Medium   | Forbidden seccomp profile                                        |\n|                  | [M-110](/internal/builtins/pss/baseline/M-110_sysctls.yml)                                           | Medium   | Unsafe sysctls                                                   |\n| PSS - Restricted | [M-111](/internal/builtins/pss/restricted/M-111_volume_types.yml)                                    | Low      | Not allowed volume type                                          |\n|                  | [M-112](/internal/builtins/pss/restricted/M-112_privilege_escalation.yml)                            | Medium   | Allowed privilege escalation                                     |\n|                  | [M-113](/internal/builtins/pss/restricted/M-113_run_as_non_root.yml)                                 | Medium   | Container could be running as root user                          |\n|                  | [M-114](/internal/builtins/pss/restricted/M-114_run_as_user.yml)                                     | Medium   | Container running as root UID                                    |\n|                  | [M-115](/internal/builtins/pss/restricted/M-115_seccomp.yml)                                         | Low      | Not allowed seccomp profile                                      |\n|                  | [M-116](/internal/builtins/pss/restricted/M-116_capabilities.yml)                                    | Low      | Not allowed added/dropped capabilities                           |\n"
  },
  {
    "path": "cmd/root.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage cmd\n\nimport (\n\t\"context\"\n\t\"flag\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/go-logr/logr\"\n\t\"github.com/spf13/cobra\"\n\t\"k8s.io/klog/v2\"\n\t\"k8s.io/klog/v2/klogr\"\n)\n\nvar noColor bool\n\n// rootCmd represents the base command when called without any subcommands\nvar rootCmd = &cobra.Command{\n\tUse:   \"marvin\",\n\tShort: \"A Kubernetes cluster scanner\",\n}\n\nfunc Execute() {\n\terr := rootCmd.Execute()\n\tif err != nil {\n\t\tos.Exit(1)\n\t}\n}\n\nfunc isKubectlPlugin() bool {\n\treturn strings.HasPrefix(filepath.Base(os.Args[0]), \"kubectl-\")\n}\n\nfunc execName() string {\n\tn := \"marvin\"\n\tif isKubectlPlugin() {\n\t\treturn \"kubectl \" + n\n\t}\n\treturn n\n}\n\nfunc init() {\n\tcobra.OnInitialize(initNoColor)\n\tif isKubectlPlugin() {\n\t\tusageTpl := strings.NewReplacer(\"{{.UseLine}}\", \"kubectl {{.UseLine}}\",\n\t\t\t\"{{.CommandPath}}\", \"kubectl {{.CommandPath}}\").Replace(rootCmd.UsageTemplate())\n\t\trootCmd.SetUsageTemplate(usageTpl)\n\t}\n\trootCmd.PersistentFlags().BoolVar(&noColor, \"no-color\", false, \"Disable color output\")\n\tvar allFlags flag.FlagSet\n\tklog.InitFlags(&allFlags)\n\tallFlags.VisitAll(func(f *flag.Flag) {\n\t\tif f.Name == \"v\" {\n\t\t\trootCmd.PersistentFlags().AddGoFlag(f)\n\t\t}\n\t})\n\trootCmd.SetContext(logr.NewContext(context.Background(), klogr.New()))\n}\n\nfunc initNoColor() {\n\tcolor.NoColor = noColor\n}\n"
  },
  {
    "path": "cmd/scan.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage cmd\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/undistro/marvin/pkg/cmd\"\n)\n\nvar (\n\tscanOptions = cmd.NewScanOptions()\n\n\t// scanCmd represents the scan command\n\tscanCmd = &cobra.Command{\n\t\tUse:   \"scan [flags]\",\n\t\tShort: \"Scan a Kubernetes cluster\",\n\t\tExample: fmt.Sprintf(`  # Scan the current cluster\n  %[1]s scan\n\n  # Scan the 'foo' namespace of the current cluster\n  %[1]s scan -n foo\n\n  # Scan the current cluster providing custom checks\n  %[1]s scan --checks ./examples/\n\n  # Scan the current cluster providing custom checks and disabling the built-in checks\n  %[1]s scan --disable-builtin --checks ./examples/\n\n  # Scan a specific cluster using a kubeconfig file\n  %[1]s scan --kubeconfig /path/to/kubeconfig.yml\n\n  # Scan the current cluster, but do not fail even if there are errors in the report\n  %[1]s scan --no-fail\n\n  # Scan the current cluster and generate output in JSON format\n  %[1]s scan -o json`, execName()),\n\t\tRunE: func(c *cobra.Command, args []string) error {\n\t\t\tif err := scanOptions.Init(c.Context()); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\thasError, err := scanOptions.Run()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif hasError && !*scanOptions.NoFail {\n\t\t\t\tos.Exit(2)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n)\n\nfunc init() {\n\trootCmd.AddCommand(scanCmd)\n\tscanOptions.AddFlags(scanCmd.Flags())\n}\n"
  },
  {
    "path": "cmd/version.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage cmd\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\n\t\"github.com/spf13/cobra\"\n\t\"sigs.k8s.io/yaml\"\n\n\t\"github.com/undistro/marvin/pkg/version\"\n)\n\nvar (\n\tversionOutput string\n\t// versionCmd represents the version command\n\tversionCmd = &cobra.Command{\n\t\tUse:   \"version\",\n\t\tShort: \"Show the version of Marvin\",\n\t\tRunE: func(c *cobra.Command, args []string) error {\n\t\t\tv := version.Get()\n\t\t\tvar s string\n\t\t\tswitch versionOutput {\n\t\t\tcase \"json\":\n\t\t\t\tb, err := json.MarshalIndent(&v, \"\", \"    \")\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\ts = string(b)\n\t\t\tcase \"yaml\":\n\t\t\t\tb, err := yaml.Marshal(&v)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\ts = string(b)\n\t\t\tdefault:\n\t\t\t\ts = v.String()\n\t\t\t}\n\t\t\tfmt.Println(s)\n\t\t\treturn nil\n\t\t},\n\t}\n)\n\nfunc init() {\n\trootCmd.AddCommand(versionCmd)\n\tversionCmd.Flags().StringVarP(&versionOutput, \"output\", \"o\", \"\", `Output format. One of: (\"json\", \"yaml\")`)\n}\n"
  },
  {
    "path": "examples/labels.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: CUSTOM-002\nseverity: Low\nmessage: \"Required labels\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\nparams:\n  requiredLabels:\n    - app\nvalidations:\n  - expression: >\n      has(object.metadata.labels) &&\n      params.requiredLabels.all(req, \n        req in object.metadata.labels\n      )\n    message: \"Pod without required labels\"\n"
  },
  {
    "path": "examples/replicas.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: CUSTOM-001\nseverity: Medium\nmessage: \"Replicas limit\"\nmatch:\n  resources:\n    - group: apps\n      version: v1\n      resource: deployments\nvalidations:\n  - expression: >\n      object.spec.replicas <= 5\n    message: \"Deployment with more than 5 replicas\"\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/undistro/marvin\n\ngo 1.25.4\n\nrequire (\n\tgithub.com/Masterminds/semver/v3 v3.4.0\n\tgithub.com/fatih/color v1.18.0\n\tgithub.com/go-logr/logr v1.4.3\n\tgithub.com/google/cel-go v0.26.0\n\tgithub.com/olekukonko/tablewriter v0.0.5\n\tgithub.com/spf13/cobra v1.10.2\n\tgithub.com/spf13/pflag v1.0.10\n\tgithub.com/stretchr/testify v1.11.1\n\tk8s.io/api v0.34.2\n\tk8s.io/apimachinery v0.34.2\n\tk8s.io/apiserver v0.34.2\n\tk8s.io/cli-runtime v0.34.2\n\tk8s.io/client-go v0.34.2\n\tk8s.io/klog/v2 v2.130.1\n\tk8s.io/utils v0.0.0-20250604170112-4c0f3b243397\n\tsigs.k8s.io/yaml v1.6.0\n)\n\nrequire (\n\tcel.dev/expr v0.24.0 // indirect\n\tgithub.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect\n\tgithub.com/antlr4-go/antlr/v4 v4.13.0 // indirect\n\tgithub.com/beorn7/perks v1.0.1 // indirect\n\tgithub.com/blang/semver/v4 v4.0.0 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/emicklei/go-restful/v3 v3.12.2 // indirect\n\tgithub.com/fxamacker/cbor/v2 v2.9.0 // indirect\n\tgithub.com/go-errors/errors v1.4.2 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.21.0 // indirect\n\tgithub.com/go-openapi/jsonreference v0.20.2 // indirect\n\tgithub.com/go-openapi/swag v0.23.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/google/btree v1.1.3 // indirect\n\tgithub.com/google/gnostic-models v0.7.0 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/json-iterator/go v1.1.12 // indirect\n\tgithub.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/mattn/go-runewidth v0.0.16 // indirect\n\tgithub.com/moby/term v0.5.0 // indirect\n\tgithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect\n\tgithub.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect\n\tgithub.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect\n\tgithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect\n\tgithub.com/peterbourgon/diskv v2.0.1+incompatible // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/prometheus/client_golang v1.22.0 // indirect\n\tgithub.com/prometheus/client_model v0.6.1 // indirect\n\tgithub.com/prometheus/common v0.62.0 // indirect\n\tgithub.com/prometheus/procfs v0.15.1 // indirect\n\tgithub.com/rivo/uniseg v0.2.0 // indirect\n\tgithub.com/stoewer/go-strcase v1.3.0 // indirect\n\tgithub.com/x448/float16 v0.8.4 // indirect\n\tgithub.com/xlab/treeprint v1.2.0 // indirect\n\tgo.opentelemetry.io/otel v1.35.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.35.0 // indirect\n\tgo.yaml.in/yaml/v2 v2.4.2 // indirect\n\tgo.yaml.in/yaml/v3 v3.0.4 // indirect\n\tgolang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect\n\tgolang.org/x/net v0.38.0 // indirect\n\tgolang.org/x/oauth2 v0.27.0 // indirect\n\tgolang.org/x/sync v0.12.0 // indirect\n\tgolang.org/x/sys v0.31.0 // indirect\n\tgolang.org/x/term v0.30.0 // indirect\n\tgolang.org/x/text v0.23.0 // indirect\n\tgolang.org/x/time v0.9.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb // indirect\n\tgoogle.golang.org/protobuf v1.36.5 // indirect\n\tgopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect\n\tgopkg.in/inf.v0 v0.9.1 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tk8s.io/component-base v0.34.2 // indirect\n\tk8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect\n\tsigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect\n\tsigs.k8s.io/kustomize/api v0.20.1 // indirect\n\tsigs.k8s.io/kustomize/kyaml v0.20.1 // indirect\n\tsigs.k8s.io/randfill v1.0.0 // indirect\n\tsigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=\ncel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=\ngithub.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=\ngithub.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=\ngithub.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=\ngithub.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=\ngithub.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=\ngithub.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=\ngithub.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=\ngithub.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=\ngithub.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=\ngithub.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=\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/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=\ngithub.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=\ngithub.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=\ngithub.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=\ngithub.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=\ngithub.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=\ngithub.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=\ngithub.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=\ngithub.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=\ngithub.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=\ngithub.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=\ngithub.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=\ngithub.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=\ngithub.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=\ngithub.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=\ngithub.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=\ngithub.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=\ngithub.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=\ngithub.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI=\ngithub.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=\ngithub.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=\ngithub.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=\ngithub.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=\ngithub.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\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/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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=\ngithub.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=\ngithub.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=\ngithub.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=\ngithub.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=\ngithub.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=\ngithub.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=\ngithub.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=\ngithub.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=\ngithub.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=\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 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=\ngithub.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=\ngithub.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=\ngithub.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=\ngithub.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=\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/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=\ngithub.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=\ngithub.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=\ngithub.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=\ngithub.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=\ngithub.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=\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_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=\ngithub.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=\ngithub.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=\ngithub.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=\ngithub.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=\ngithub.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=\ngithub.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=\ngithub.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=\ngithub.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=\ngithub.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=\ngithub.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=\ngithub.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=\ngithub.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=\ngithub.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=\ngithub.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=\ngithub.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=\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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=\ngithub.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\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/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=\ngithub.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=\ngithub.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=\ngithub.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngo.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=\ngo.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=\ngo.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=\ngo.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=\ngo.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\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/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=\ngolang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=\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/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=\ngolang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=\ngolang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=\ngolang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=\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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=\ngolang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=\ngolang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=\ngolang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=\ngolang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=\ngolang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=\ngolang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=\ngolang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=\ngolang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=\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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=\ngoogle.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=\ngoogle.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=\ngopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=\ngopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=\ngopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=\nk8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY=\nk8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw=\nk8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4=\nk8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=\nk8s.io/apiserver v0.34.2 h1:2/yu8suwkmES7IzwlehAovo8dDE07cFRC7KMDb1+MAE=\nk8s.io/apiserver v0.34.2/go.mod h1:gqJQy2yDOB50R3JUReHSFr+cwJnL8G1dzTA0YLEqAPI=\nk8s.io/cli-runtime v0.34.2 h1:cct1GEuWc3IyVT8MSCoIWzRGw9HJ/C5rgP32H60H6aE=\nk8s.io/cli-runtime v0.34.2/go.mod h1:X13tsrYexYUCIq8MarCBy8lrm0k0weFPTpcaNo7lms4=\nk8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M=\nk8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE=\nk8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ=\nk8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM=\nk8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=\nk8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=\nk8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=\nk8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=\nk8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=\nk8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=\nsigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=\nsigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=\nsigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I=\nsigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM=\nsigs.k8s.io/kustomize/kyaml v0.20.1 h1:PCMnA2mrVbRP3NIB6v9kYCAc38uvFLVs8j/CD567A78=\nsigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po=\nsigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=\nsigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=\nsigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=\nsigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=\nsigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=\nsigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=\n"
  },
  {
    "path": "install.sh",
    "content": "#!/bin/sh\n\nset -e\n# Code generated by godownloader on 2023-02-28T17:14:26Z. DO NOT EDIT.\n#\n\nusage() {\n  this=$1\n  cat <<EOF\n$this: download go binaries for undistro/marvin\n\nUsage: $this [-b] bindir [-d] [tag]\n  -b sets bindir or installation directory, Defaults to ./bin\n  -d turns on debug logging\n   [tag] is a tag from\n   https://github.com/undistro/marvin/releases\n   If tag is missing, then the latest will be used.\n\n Generated by godownloader\n  https://github.com/goreleaser/godownloader\n\nEOF\n  exit 2\n}\n\nparse_args() {\n  #BINDIR is ./bin unless set be ENV\n  # over-ridden by flag below\n\n  BINDIR=${BINDIR:-./bin}\n  while getopts \"b:dh?x\" arg; do\n    case \"$arg\" in\n      b) BINDIR=\"$OPTARG\" ;;\n      d) log_set_priority 10 ;;\n      h | \\?) usage \"$0\" ;;\n      x) set -x ;;\n    esac\n  done\n  shift $((OPTIND - 1))\n  TAG=$1\n}\n# this function wraps all the destructive operations\n# if a curl|bash cuts off the end of the script due to\n# network, either nothing will happen or will syntax error\n# out preventing half-done work\nexecute() {\n  tmpdir=$(mktemp -d)\n  log_debug \"downloading files into ${tmpdir}\"\n  http_download \"${tmpdir}/${TARBALL}\" \"${TARBALL_URL}\"\n  http_download \"${tmpdir}/${CHECKSUM}\" \"${CHECKSUM_URL}\"\n  hash_sha256_verify \"${tmpdir}/${TARBALL}\" \"${tmpdir}/${CHECKSUM}\"\n  srcdir=\"${tmpdir}\"\n  (cd \"${tmpdir}\" && untar \"${TARBALL}\")\n  test ! -d \"${BINDIR}\" && install -d \"${BINDIR}\"\n  for binexe in $BINARIES; do\n    if [ \"$OS\" = \"windows\" ]; then\n      binexe=\"${binexe}.exe\"\n    fi\n    install \"${srcdir}/${binexe}\" \"${BINDIR}/\"\n    log_info \"installed ${BINDIR}/${binexe}\"\n  done\n  rm -rf \"${tmpdir}\"\n}\nget_binaries() {\n  case \"$PLATFORM\" in\n    darwin/386) BINARIES=\"marvin\" ;;\n    darwin/amd64) BINARIES=\"marvin\" ;;\n    linux/386) BINARIES=\"marvin\" ;;\n    linux/amd64) BINARIES=\"marvin\" ;;\n    windows/386) BINARIES=\"marvin\" ;;\n    windows/amd64) BINARIES=\"marvin\" ;;\n    *)\n      log_crit \"platform $PLATFORM is not supported.  Make sure this script is up-to-date and file request at https://github.com/${PREFIX}/issues/new\"\n      exit 1\n      ;;\n  esac\n}\ntag_to_version() {\n  if [ -z \"${TAG}\" ]; then\n    log_info \"checking GitHub for latest tag\"\n  else\n    log_info \"checking GitHub for tag '${TAG}'\"\n  fi\n  REALTAG=$(github_release \"$OWNER/$REPO\" \"${TAG}\") && true\n  if test -z \"$REALTAG\"; then\n    log_crit \"unable to find '${TAG}' - use 'latest' or see https://github.com/${PREFIX}/releases for details\"\n    exit 1\n  fi\n  # if version starts with 'v', remove it\n  TAG=\"$REALTAG\"\n  VERSION=${TAG#v}\n}\nadjust_format() {\n  # change format (tar.gz or zip) based on OS\n  true\n}\nadjust_os() {\n  # adjust archive name based on OS\n  case ${OS} in\n    386) OS=i386 ;;\n    amd64) OS=x86_64 ;;\n    darwin) OS=Darwin ;;\n    linux) OS=Linux ;;\n    windows) OS=Windows ;;\n  esac\n  true\n}\nadjust_arch() {\n  # adjust archive name based on ARCH\n  case ${ARCH} in\n    386) ARCH=i386 ;;\n    amd64) ARCH=x86_64 ;;\n    darwin) ARCH=Darwin ;;\n    linux) ARCH=Linux ;;\n    windows) ARCH=Windows ;;\n  esac\n  true\n}\n\ncat /dev/null <<EOF\n------------------------------------------------------------------------\nhttps://github.com/client9/shlib - portable posix shell functions\nPublic domain - http://unlicense.org\nhttps://github.com/client9/shlib/blob/master/LICENSE.md\nbut credit (and pull requests) appreciated.\n------------------------------------------------------------------------\nEOF\nis_command() {\n  command -v \"$1\" >/dev/null\n}\nechoerr() {\n  echo \"$@\" 1>&2\n}\nlog_prefix() {\n  echo \"$0\"\n}\n_logp=6\nlog_set_priority() {\n  _logp=\"$1\"\n}\nlog_priority() {\n  if test -z \"$1\"; then\n    echo \"$_logp\"\n    return\n  fi\n  [ \"$1\" -le \"$_logp\" ]\n}\nlog_tag() {\n  case $1 in\n    0) echo \"emerg\" ;;\n    1) echo \"alert\" ;;\n    2) echo \"crit\" ;;\n    3) echo \"err\" ;;\n    4) echo \"warning\" ;;\n    5) echo \"notice\" ;;\n    6) echo \"info\" ;;\n    7) echo \"debug\" ;;\n    *) echo \"$1\" ;;\n  esac\n}\nlog_debug() {\n  log_priority 7 || return 0\n  echoerr \"$(log_prefix)\" \"$(log_tag 7)\" \"$@\"\n}\nlog_info() {\n  log_priority 6 || return 0\n  echoerr \"$(log_prefix)\" \"$(log_tag 6)\" \"$@\"\n}\nlog_err() {\n  log_priority 3 || return 0\n  echoerr \"$(log_prefix)\" \"$(log_tag 3)\" \"$@\"\n}\nlog_crit() {\n  log_priority 2 || return 0\n  echoerr \"$(log_prefix)\" \"$(log_tag 2)\" \"$@\"\n}\nuname_os() {\n  os=$(uname -s | tr '[:upper:]' '[:lower:]')\n  case \"$os\" in\n    cygwin_nt*) os=\"windows\" ;;\n    mingw*) os=\"windows\" ;;\n    msys_nt*) os=\"windows\" ;;\n  esac\n  echo \"$os\"\n}\nuname_arch() {\n  arch=$(uname -m)\n  case $arch in\n    x86_64) arch=\"amd64\" ;;\n    x86) arch=\"386\" ;;\n    i686) arch=\"386\" ;;\n    i386) arch=\"386\" ;;\n    aarch64) arch=\"arm64\" ;;\n    armv5*) arch=\"armv5\" ;;\n    armv6*) arch=\"armv6\" ;;\n    armv7*) arch=\"armv7\" ;;\n  esac\n  echo ${arch}\n}\nuname_os_check() {\n  os=$(uname_os)\n  case \"$os\" in\n    darwin) return 0 ;;\n    dragonfly) return 0 ;;\n    freebsd) return 0 ;;\n    linux) return 0 ;;\n    android) return 0 ;;\n    nacl) return 0 ;;\n    netbsd) return 0 ;;\n    openbsd) return 0 ;;\n    plan9) return 0 ;;\n    solaris) return 0 ;;\n    windows) return 0 ;;\n  esac\n  log_crit \"uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib\"\n  return 1\n}\nuname_arch_check() {\n  arch=$(uname_arch)\n  case \"$arch\" in\n    386) return 0 ;;\n    amd64) return 0 ;;\n    arm64) return 0 ;;\n    armv5) return 0 ;;\n    armv6) return 0 ;;\n    armv7) return 0 ;;\n    ppc64) return 0 ;;\n    ppc64le) return 0 ;;\n    mips) return 0 ;;\n    mipsle) return 0 ;;\n    mips64) return 0 ;;\n    mips64le) return 0 ;;\n    s390x) return 0 ;;\n    amd64p32) return 0 ;;\n  esac\n  log_crit \"uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib\"\n  return 1\n}\nuntar() {\n  tarball=$1\n  case \"${tarball}\" in\n    *.tar.gz | *.tgz) tar --no-same-owner -xzf \"${tarball}\" ;;\n    *.tar) tar --no-same-owner -xf \"${tarball}\" ;;\n    *.zip) unzip \"${tarball}\" ;;\n    *)\n      log_err \"untar unknown archive format for ${tarball}\"\n      return 1\n      ;;\n  esac\n}\nhttp_download_curl() {\n  local_file=$1\n  source_url=$2\n  header=$3\n  if [ -z \"$header\" ]; then\n    code=$(curl -w '%{http_code}' -sL -o \"$local_file\" \"$source_url\")\n  else\n    code=$(curl -w '%{http_code}' -sL -H \"$header\" -o \"$local_file\" \"$source_url\")\n  fi\n  if [ \"$code\" != \"200\" ]; then\n    log_debug \"http_download_curl received HTTP status $code\"\n    return 1\n  fi\n  return 0\n}\nhttp_download_wget() {\n  local_file=$1\n  source_url=$2\n  header=$3\n  if [ -z \"$header\" ]; then\n    wget -q -O \"$local_file\" \"$source_url\"\n  else\n    wget -q --header \"$header\" -O \"$local_file\" \"$source_url\"\n  fi\n}\nhttp_download() {\n  log_debug \"http_download $2\"\n  if is_command curl; then\n    http_download_curl \"$@\"\n    return\n  elif is_command wget; then\n    http_download_wget \"$@\"\n    return\n  fi\n  log_crit \"http_download unable to find wget or curl\"\n  return 1\n}\nhttp_copy() {\n  tmp=$(mktemp)\n  http_download \"${tmp}\" \"$1\" \"$2\" || return 1\n  body=$(cat \"$tmp\")\n  rm -f \"${tmp}\"\n  echo \"$body\"\n}\ngithub_release() {\n  owner_repo=$1\n  version=$2\n  test -z \"$version\" && version=\"latest\"\n  giturl=\"https://github.com/${owner_repo}/releases/${version}\"\n  json=$(http_copy \"$giturl\" \"Accept:application/json\")\n  test -z \"$json\" && return 1\n  version=$(echo \"$json\" | tr -s '\\n' ' ' | sed 's/.*\"tag_name\":\"//' | sed 's/\".*//')\n  test -z \"$version\" && return 1\n  echo \"$version\"\n}\nhash_sha256() {\n  TARGET=${1:-/dev/stdin}\n  if is_command gsha256sum; then\n    hash=$(gsha256sum \"$TARGET\") || return 1\n    echo \"$hash\" | cut -d ' ' -f 1\n  elif is_command sha256sum; then\n    hash=$(sha256sum \"$TARGET\") || return 1\n    echo \"$hash\" | cut -d ' ' -f 1\n  elif is_command shasum; then\n    hash=$(shasum -a 256 \"$TARGET\" 2>/dev/null) || return 1\n    echo \"$hash\" | cut -d ' ' -f 1\n  elif is_command openssl; then\n    hash=$(openssl -dst openssl dgst -sha256 \"$TARGET\") || return 1\n    echo \"$hash\" | cut -d ' ' -f a\n  else\n    log_crit \"hash_sha256 unable to find command to compute sha-256 hash\"\n    return 1\n  fi\n}\nhash_sha256_verify() {\n  TARGET=$1\n  checksums=$2\n  if [ -z \"$checksums\" ]; then\n    log_err \"hash_sha256_verify checksum file not specified in arg2\"\n    return 1\n  fi\n  BASENAME=${TARGET##*/}\n  want=$(grep \"${BASENAME}\" \"${checksums}\" 2>/dev/null | tr '\\t' ' ' | cut -d ' ' -f 1)\n  if [ -z \"$want\" ]; then\n    log_err \"hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'\"\n    return 1\n  fi\n  got=$(hash_sha256 \"$TARGET\")\n  if [ \"$want\" != \"$got\" ]; then\n    log_err \"hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got\"\n    return 1\n  fi\n}\ncat /dev/null <<EOF\n------------------------------------------------------------------------\nEnd of functions from https://github.com/client9/shlib\n------------------------------------------------------------------------\nEOF\n\nPROJECT_NAME=\"marvin\"\nOWNER=undistro\nREPO=\"marvin\"\nBINARY=marvin\nFORMAT=tar.gz\nOS=$(uname_os)\nARCH=$(uname_arch)\nPREFIX=\"$OWNER/$REPO\"\n\n# use in logging routines\nlog_prefix() {\n\techo \"$PREFIX\"\n}\nPLATFORM=\"${OS}/${ARCH}\"\nGITHUB_DOWNLOAD=https://github.com/${OWNER}/${REPO}/releases/download\n\nuname_os_check \"$OS\"\nuname_arch_check \"$ARCH\"\n\nparse_args \"$@\"\n\nget_binaries\n\ntag_to_version\n\nadjust_format\n\nadjust_os\n\nadjust_arch\n\nlog_info \"found version: ${VERSION} for ${TAG}/${OS}/${ARCH}\"\n\nNAME=${PROJECT_NAME}_${OS}_${ARCH}\nTARBALL=${NAME}.${FORMAT}\nTARBALL_URL=${GITHUB_DOWNLOAD}/${TAG}/${TARBALL}\nCHECKSUM=checksums.txt\nCHECKSUM_URL=${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM}\n\n\nexecute\n"
  },
  {
    "path": "internal/builtins/cis/M-500_workload_in_default_namespace.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n\nid: M-500\nslug: workload-in-default-namespace\nseverity: Medium\nmessage: \"Workload in default namespace\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: object.metadata.?namespace.orValue(\"default\") != \"default\"\n"
  },
  {
    "path": "internal/builtins/cis/M-500_workload_in_default_namespace_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"Namespace set to default\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n      namespace: default\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Namespace not specified\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          volumes: []\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Namespace specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n      namespace: fooserver\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          volumes: []\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx"
  },
  {
    "path": "internal/builtins/embed.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage builtins\n\nimport (\n\t\"embed\"\n)\n\n//go:embed *\nvar EmbedChecksFS embed.FS\n"
  },
  {
    "path": "internal/builtins/embed_test.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage builtins\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestEmbedChecks(t *testing.T) {\n\tentries, err := EmbedChecksFS.ReadDir(\".\")\n\tassert.NoError(t, err)\n\tassert.Greater(t, len(entries), 0)\n}\n"
  },
  {
    "path": "internal/builtins/general/M-400_image_tagged_latest.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-400\nslug: image-tagged-latest\nseverity: Medium\nmessage: \"Image tagged latest\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      allContainers.all(container,\n        container.image.contains(\":\") && // image digest contains \":\"\n        [container.image.substring(container.image.lastIndexOf(\":\")+1)].all(image,\n          !image.contains(\"/\") && !(image in [\"latest\", \"\"])\n        )\n      )\n"
  },
  {
    "path": "internal/builtins/general/M-400_image_tagged_latest_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"untagged\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"registry + untagged image\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: registry.com/nginx\n\n- name: \"registry + port + untagged image\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: registry.com:10443/nginx\n\n- name: \"latest tag\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx:latest\n\n- name: \"registry + image tagged latest\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: registry.com/nginx:latest\n\n- name: \"registry + port + image tagged latest\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: registry.com:10443/nginx:latest\n\n- name: \"all ok\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - image: nginx:1\n          name: cont1\n        - image: nginx:1.25\n          name: cont2\n        - image: nginx@sha256:593dac25b7733ffb7afe1a72649a43e574778bf025ad60514ef40f6b5d606247\n          name: cont3\n        - image: registry.com/images/nginx:1\n          name: cont4\n        - image: registry.com:10443/nginx:1.25\n          name: cont5\n        - image: registry.com/nginx@sha256:593dac25b7733ffb7afe1a72649a43e574778bf025ad60514ef40f6b5d606247\n          name: cont6\n"
  },
  {
    "path": "internal/builtins/general/M-401_unmanaged_pod.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-401\nslug: unmanaged-pod\nseverity: Low\nmessage: \"Unmanaged Pod\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\nvariables:\n  - name: owners\n    expression: object.metadata.?ownerReferences.orValue([])\nvalidations:\n  - expression: >\n      variables.owners != null && variables.owners.exists(o, o.?controller.orValue(false) == true)\n"
  },
  {
    "path": "internal/builtins/general/M-401_unmanaged_pod_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"ok\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx-7f7b76bc5b-6vb88\n      ownerReferences:\n        - apiVersion: apps/v1\n          blockOwnerDeletion: true\n          controller: true\n          kind: ReplicaSet\n          name: nginx-7f7b76bc5b\n          uid: daf540b6-b932-4b2d-a9b1-fa9cdfbff38b\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"controller false\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx-7f7b76bc5b-6vb88\n      ownerReferences:\n        - apiVersion: apps/v1\n          blockOwnerDeletion: true\n          controller: false\n          kind: ReplicaSet\n          name: nginx-7f7b76bc5b\n          uid: daf540b6-b932-4b2d-a9b1-fa9cdfbff38b\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"unmanaged\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"ownerReferences set to null\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      ownerReferences: null\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"ownerReferences empty\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      ownerReferences: []\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n"
  },
  {
    "path": "internal/builtins/general/M-402_readiness_probe.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-402\nslug: readiness-probe\nseverity: Medium\nmessage: \"Readiness and startup probe not configured\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\nvariables:\n  - name: owners\n    expression: object.metadata.?ownerReferences.orValue([])\nvalidations:\n  - expression: >\n      (\n        object.kind == \"Pod\" && \n        variables.owners != null && \n        variables.owners.exists(o, o.?kind.orValue(\"\") == \"Job\" && o.?apiVersion.orValue(\"\") == \"batch/v1\")\n      )\n      || \n      podSpec.containers.all(c, has(c.readinessProbe) || has(c.startupProbe))\n"
  },
  {
    "path": "internal/builtins/general/M-402_readiness_probe_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"readinessProbe not specified\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"readinessProbe not specified in managed pod\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx-7f7b76bc5b-6vb88\n      ownerReferences:\n        - apiVersion: apps/v1\n          blockOwnerDeletion: true\n          controller: true\n          kind: ReplicaSet\n          name: nginx-7f7b76bc5b\n          uid: daf540b6-b932-4b2d-a9b1-fa9cdfbff38b\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"readinessProbe set to null\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          readinessProbe: null\n\n- name: \"readiness probe configured\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          readinessProbe:\n            httpGet:\n              port: 80\n\n- name: \"startup probe configured\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          startupProbe:\n            httpGet:\n              port: 80\n\n- name: \"managed pod with readiness probe configured\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx-7f7b76bc5b-6vb88\n      ownerReferences:\n        - apiVersion: apps/v1\n          blockOwnerDeletion: true\n          controller: true\n          kind: ReplicaSet\n          name: nginx-7f7b76bc5b\n          uid: daf540b6-b932-4b2d-a9b1-fa9cdfbff38b\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          readinessProbe:\n            httpGet:\n              port: 80\n\n- name: \"managed pod with startup probe configured\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx-7f7b76bc5b-6vb88\n      ownerReferences:\n        - apiVersion: apps/v1\n          blockOwnerDeletion: true\n          controller: true\n          kind: ReplicaSet\n          name: nginx-7f7b76bc5b\n          uid: daf540b6-b932-4b2d-a9b1-fa9cdfbff38b\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          startupProbe:\n            httpGet:\n              port: 80\n\n- name: \"job\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: job-28119672-9sv4f\n      labels:\n        app: job\n      ownerReferences:\n      - apiVersion: batch/v1\n        blockOwnerDeletion: true\n        controller: true\n        kind: Job\n        name: job-28119672\n        uid: 3c506232-bf9b-475b-add5-d5e850ba6ceb      \n    spec:\n      containers:\n      - name: job\n        image: job\n\n- name: \"ownerReferences set to null\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      ownerReferences: null  \n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n"
  },
  {
    "path": "internal/builtins/general/M-403_liveness_probe.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-403\nslug: liveness-probe\nseverity: Medium\nmessage: \"Liveness probe not configured\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\nvariables:\n  - name: owners\n    expression: object.metadata.?ownerReferences.orValue([])\nvalidations:\n  - expression: >\n      (\n        object.kind == \"Pod\" && \n        variables.owners != null && \n        variables.owners.exists(o, o.?kind.orValue(\"\") == \"Job\" && o.?apiVersion.orValue(\"\") == \"batch/v1\")\n      )\n      || \n      podSpec.containers.all(c, has(c.livenessProbe))\n"
  },
  {
    "path": "internal/builtins/general/M-403_liveness_probe_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"livenessProbe not specified\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"livenessProbe not specified in managed pod\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx-7f7b76bc5b-6vb88\n      ownerReferences:\n        - apiVersion: apps/v1\n          blockOwnerDeletion: true\n          controller: true\n          kind: ReplicaSet\n          name: nginx-7f7b76bc5b\n          uid: daf540b6-b932-4b2d-a9b1-fa9cdfbff38b\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"livenessProbe set to null\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          livenessProbe: null\n\n- name: \"ok\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          livenessProbe:\n            httpGet:\n              port: 80\n\n- name: \"managed pod ok\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx-7f7b76bc5b-6vb88\n      ownerReferences:\n        - apiVersion: apps/v1\n          blockOwnerDeletion: true\n          controller: true\n          kind: ReplicaSet\n          name: nginx-7f7b76bc5b\n          uid: daf540b6-b932-4b2d-a9b1-fa9cdfbff38b\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          livenessProbe:\n            httpGet:\n              port: 80\n\n- name: \"job\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: job-28119672-9sv4f\n      labels:\n        app: job\n      ownerReferences:\n      - apiVersion: batch/v1\n        blockOwnerDeletion: true\n        controller: true\n        kind: Job\n        name: job-28119672\n        uid: 3c506232-bf9b-475b-add5-d5e850ba6ceb      \n    spec:\n      containers:\n        - name: job\n          image: job\n"
  },
  {
    "path": "internal/builtins/general/M-404_memory_requests.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-404\nslug: memory-requests\nseverity: Medium\nmessage: \"Memory requests not specified\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      allContainers.all(c, c.?resources.?requests.?memory.orValue(\"\") != \"\")\n"
  },
  {
    "path": "internal/builtins/general/M-404_memory_requests_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"resources not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"resources requests not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            limits:\n              memory: 128Mi\n\n- name: \"memory requests not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            requests:\n              cpu: 10m\n\n- name: \"ok\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            requests:\n              cpu: 10m\n              memory: 64Mi\n"
  },
  {
    "path": "internal/builtins/general/M-405_cpu_requests.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-405\nslug: cpu-requests\nseverity: Medium\nmessage: \"CPU requests not specified\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      allContainers.all(c, c.?resources.?requests.?cpu.orValue(\"\") != \"\")\n"
  },
  {
    "path": "internal/builtins/general/M-405_cpu_requests_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"resources not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"resources requests not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            limits:\n              memory: 128Mi\n\n- name: \"cpu requests not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            requests:\n              memory: 64Mi\n\n- name: \"ok\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            requests:\n              cpu: 10m\n              memory: 64Mi\n"
  },
  {
    "path": "internal/builtins/general/M-406_memory_limit.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-406\nslug: memory-limit\nseverity: Medium\nmessage: \"Memory not limited\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      allContainers.all(c, c.?resources.?limits.?memory.orValue(\"\") != \"\")\n"
  },
  {
    "path": "internal/builtins/general/M-406_memory_limit_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"resources not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"resources limits not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            requests:\n              memory: 64Mi\n\n- name: \"memory limits not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            limits:\n              cpu: 500m\n\n- name: \"ok\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            limits:\n              memory: 128Mi\n"
  },
  {
    "path": "internal/builtins/general/M-407_cpu_limit.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-407\nslug: cpu-limit\nseverity: Medium\nmessage: \"CPU not limited\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      allContainers.all(c, c.?resources.?limits.?cpu.orValue(\"\") != \"\")\n"
  },
  {
    "path": "internal/builtins/general/M-407_cpu_limit_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"resources not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"resources limits not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            requests:\n              cpu: 5m\n\n- name: \"CPU limits not set\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            limits:\n              memory: 128Mi\n\n- name: \"ok\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          resources:\n            limits:\n              cpu: 500m\n"
  },
  {
    "path": "internal/builtins/general/M-408_sudo_container_entrypoint.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-408\nslug: sudo-container-entrypoint\nseverity: Medium\nmessage: \"Sudo in container entrypoint\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      allContainers.all(c,\n        c.?command.orValue([]).all(cmd, !cmd.contains(\"sudo\"))\n      )\n"
  },
  {
    "path": "internal/builtins/general/M-408_sudo_container_entrypoint_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"command not set\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"command without sudo\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          command: [\"printenv\"]\n          resources:\n            requests:\n              cpu: 5m\n\n\n- name: \"command with sudo on entrypoint\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          command: [\"sudo printenv\"]\n          resources:\n            limits:\n              memory: 128Mi"
  },
  {
    "path": "internal/builtins/general/M-409_deprecated_image_registry.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-409\nslug: deprecated-image-registry\nseverity: Medium\nmessage: \"Deprecated image registry\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      allContainers.all(c, !c.image.contains(\"k8s.grc.io\"))\n"
  },
  {
    "path": "internal/builtins/general/M-409_deprecated_image_registry_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"registry not defined\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"registry deprecated\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: reg-fail\n          image: k8s.grc.io/pause\n\n- name: \"registry deprecated top\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: reg-fail\n          image: k8s.grc.io/pause\n        - name: reg-ok1\n          image: ok-registry.com:80/nginx@sha256:asdf\n        - name:  reg-ok2\n          image: also-ok-registry.com:80/nginx@sha256:asdf\n\n- name: \"registry deprecated middle\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: reg-ok1\n          image: ok-registry.com:80/nginx@sha256:asdf\n        - name: reg-fail\n          image: k8s.grc.io/pause\n        - name:  reg-ok2\n          image: also-ok-registry.com:80/nginx@sha256:asdf\n\n- name: \"registry deprecated bottom\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: reg-ok1\n          image: ok-registry.com:80/nginx@sha256:asdf\n        - name:  reg-ok2\n          image: also-ok-registry.com:80/nginx@sha256:asdf\n        - name: reg-fail\n          image: k8s.grc.io/pause\n\n\n"
  },
  {
    "path": "internal/builtins/general/M-410_resource_using_invalid_restartpolicy.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-410\nslug: resource-using-invalid-restartpolicy\nseverity: Medium\nmessage: \"Not allowed restartPolicy\"\nmatch:\n  resources:\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: replicasets\nvalidations:\n  - expression: >\n      podSpec.?restartPolicy.orValue(\"Always\") == 'Always'"
  },
  {
    "path": "internal/builtins/general/M-410_resource_using_invalid_restartpolicy_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"restartPolicy not defined\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n\n\n- name: \"restartPolicy set OnFailure\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n    spec:\n      containers:\n      - name: nginx\n        image: nginx\n      restartPolicy: OnFailure\n\n\n- name: \"restartPolicy set Always\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n      restartPolicy: Always\n\n\n- name: \"restartPolicy set Never\"\n  pass: false\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n      restartPolicy: Never\n"
  },
  {
    "path": "internal/builtins/general/M-411_role_binding_referencing_anonymous_or_unauthenticated.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-411\nslug: role-binding-referencing-anonymous-or-unauthenticated\nseverity: Medium\nmessage: \"Role Binding referencing anonymous user or unauthenticated group\"\nmatch:\n  resources:\n    - group: \"rbac.authorization.k8s.io\"\n      version: v1\n      resource: rolebindings\n    - group: \"rbac.authorization.k8s.io\"\n      version: v1\n      resource: clusterrolebindings\nvalidations:\n  - expression: >\n      !has(object.subjects) ||\n      object.subjects.all(subject,\n        !(subject.kind == \"User\" && subject.name == \"system:anonymous\") &&\n        !(subject.kind == \"Group\" && subject.name == \"system:unauthenticated\")\n      )\n"
  },
  {
    "path": "internal/builtins/general/M-411_role_binding_referencing_anonymous_or_unauthenticated_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"anonymous user in role binding\"\n  pass: false\n  input: |\n    apiVersion: rbac.authorization.k8s.io/v1\n    kind: RoleBinding\n    metadata:\n      name: binding-name\n      namespace: binding-namespace\n    roleRef:\n      apiGroup: rbac.authorization.k8s.io\n      kind: Role\n      name: role-name\n    subjects:\n    - apiGroup: rbac.authorization.k8s.io\n      kind: User\n      name: system:anonymous\n    - kind: ServiceAccount\n      name: zora-operator\n      namespace: zora-system\n\n- name: \"anonymous user in cluster role binding\"\n  pass: false\n  input: |\n    apiVersion: rbac.authorization.k8s.io/v1\n    kind: ClusterRoleBinding\n    metadata:\n      name: binding-name\n    roleRef:\n      apiGroup: rbac.authorization.k8s.io\n      kind: ClusterRole\n      name: role-name\n    subjects:\n    - apiGroup: rbac.authorization.k8s.io\n      kind: User\n      name: system:anonymous\n    - kind: ServiceAccount\n      name: zora-operator\n      namespace: zora-system\n\n- name: \"unauthenticated group in role binding\"\n  pass: false\n  input: |\n    apiVersion: rbac.authorization.k8s.io/v1\n    kind: RoleBinding\n    metadata:\n      name: binding-name\n      namespace: binding-namespace\n    roleRef:\n      apiGroup: rbac.authorization.k8s.io\n      kind: Role\n      name: role-name\n    subjects:\n    - apiGroup: rbac.authorization.k8s.io\n      kind: Group\n      name: system:unauthenticated\n    - kind: ServiceAccount\n      name: zora-operator\n      namespace: zora-system\n\n- name: \"unauthenticated group in cluster role binding\"\n  pass: false\n  input: |\n    apiVersion: rbac.authorization.k8s.io/v1\n    kind: ClusterRoleBinding\n    metadata:\n      name: binding-name\n    roleRef:\n      apiGroup: rbac.authorization.k8s.io\n      kind: ClusterRole\n      name: role-name\n    subjects:\n    - apiGroup: rbac.authorization.k8s.io\n      kind: Group\n      name: system:unauthenticated\n    - kind: ServiceAccount\n      name: zora-operator\n      namespace: zora-system\n\n- name: \"valid role binding\"\n  pass: true\n  input: |\n    apiVersion: rbac.authorization.k8s.io/v1\n    kind: RoleBinding\n    metadata:\n      name: binding-name\n    roleRef:\n      apiGroup: rbac.authorization.k8s.io\n      kind: Role\n      name: role-name\n    subjects:\n    - kind: ServiceAccount\n      name: zora-operator\n      namespace: zora-system\n\n- name: \"valid cluster role binding\"\n  pass: true\n  input: |\n    apiVersion: rbac.authorization.k8s.io/v1\n    kind: ClusterRoleBinding\n    metadata:\n      name: binding-name\n    roleRef:\n      apiGroup: rbac.authorization.k8s.io\n      kind: ClusterRole\n      name: role-name\n    subjects:\n    - kind: ServiceAccount\n      name: zora-operator\n      namespace: zora-system\n"
  },
  {
    "path": "internal/builtins/mitre/M-200_allowed_registries.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-200\nslug: allowed-registries\nseverity: Medium\nmessage: \"Image registry not allowed\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nparams:\n  # use 'docker.io' for Docker Hub\n  allowedRegistries: []\nvalidations:\n  - expression: >\n      size(params.allowedRegistries) == 0 ||\n      allContainers.all(container,\n        params.allowedRegistries.exists(registry,\n          ((registry in ['docker.io', 'docker.io/library']) && !container.image.contains('/')) ||\n          container.image.startsWith(registry)\n        )\n      )\n    message: \"Container image registry not allowed\"\n"
  },
  {
    "path": "internal/builtins/mitre/M-200_allowed_registries_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"allowedRegistries param not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"docker.io allowed\"\n  pass: true\n  params:\n    allowedRegistries: [docker.io, ghcr.io]\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"docker.io allowed and container using full name\"\n  pass: true\n  params:\n    allowedRegistries: [docker.io, ghcr.io]\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: docker.io/library/nginx:latest\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"ghcr.io allowed\"\n  pass: true\n  params:\n    allowedRegistries: [docker.io, ghcr.io]\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: ghcr.io/nginx/nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"docker.io/library allowed\"\n  pass: true\n  params:\n    allowedRegistries: [\"docker.io/library\", ghcr.io]\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"not allowed\"\n  pass: false\n  message: \"Container image registry not allowed\"\n  params:\n    allowedRegistries: [docker.io, ghcr.io]\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: gcr.io/nginx\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/mitre/M-201_app_credentials.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-201\nslug: app-credentials\nseverity: High\nmessage: \"Application credentials stored in configuration files\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\n    - group: \"\"\n      version: v1\n      resource: configmaps\nparams:\n  sensitiveKeys:\n    - aws_access_key_id\n    - aws_secret_access_key\n    - azure_batchai_storage_account\n    - azure_batchai_storage_key\n    - azure_batch_account\n    - azure_batch_key\n    - secret\n    - key\n    - password\n    - pwd\n    - token\n    - jwt\n    - bearer\n    - credential\n  sensitiveValues:\n    - BEGIN \\w+ PRIVATE KEY\n    - PRIVATE KEY\n    - eyJhbGciO\n    - JWT\n    - Bearer\n    - key\n    - secret\nvalidations:\n  - expression: >\n      object.kind != 'ConfigMap' ||\n      !has(object.data) ||\n      object.data.all(key, \n        !params.sensitiveKeys.exists(sensitiveKey,\n          key.lowerAscii().contains(sensitiveKey)\n        ) &&\n        !params.sensitiveValues.exists(sensitiveValue,\n          object.data[key].matches(sensitiveValue)\n        )\n      )\n    message: \"ConfigMap could be storing sensitive data\"\n\n  - expression: >\n      allContainers.all(container,\n        !has(container.env) ||\n        container.env.all(env,\n          !params.sensitiveKeys.exists(sensitiveKey,\n            env.name.lowerAscii().contains(sensitiveKey)\n          ) &&\n          (!has(env.value) ||\n            !params.sensitiveValues.exists(sensitiveValue,\n              env.value.matches(sensitiveValue)\n            )\n          )\n        )\n      )\n    message: \"Container could be storing sensitive data as environment variable\"\n"
  },
  {
    "path": "internal/builtins/mitre/M-201_app_credentials_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"ConfigMap with no sensitive keys and values\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: ConfigMap\n    metadata:\n      name: config\n    data:\n      config: value\n      foo: bar\n      file.yaml: content\n\n- name: \"ConfigMap with no data\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: ConfigMap\n    metadata:\n      name: config\n\n- name: \"ConfigMap with empty data\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: ConfigMap\n    metadata:\n      name: config\n    data: {}\n\n- name: \"ConfigMap with sensitive key\"\n  pass: false\n  message: \"ConfigMap could be storing sensitive data\"\n  input: |\n    apiVersion: v1\n    kind: ConfigMap\n    metadata:\n      name: config\n    data:\n      config: value\n      key: value\n\n- name: \"ConfigMap with sensitive value\"\n  pass: false\n  message: \"ConfigMap could be storing sensitive data\"\n  input: |\n    apiVersion: v1\n    kind: ConfigMap\n    metadata:\n      name: config\n    data:\n      foo: bar\n      config: \"PRIVATE KEY\"\n\n- name: \"Container with no env\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container with empty env\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              env: []\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container with no sensitive env\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              env:\n                - name: foo\n                  value: bar\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container with sensitive env name\"\n  pass: false\n  message: \"Container could be storing sensitive data as environment variable\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              env:\n                - name: foo\n                  value: bar\n                - name: key\n                  value: bar\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container with sensitive env value\"\n  pass: false\n  message: \"Container could be storing sensitive data as environment variable\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              env:\n                - name: foo\n                  value: \"Bearer token\"\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/mitre/M-202_auto_mount_service_account_token.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-202\nslug: auto-mount-service-account-token\nseverity: Low\nmessage: \"Automounted service account token\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      podSpec.?automountServiceAccountToken.orValue(true) == false\n    message: \"Pod with Service Account token mounted automatically\"\n"
  },
  {
    "path": "internal/builtins/mitre/M-202_auto_mount_service_account_token_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"automountServiceAccountToken not specified\"\n  pass: false\n  message: \"Pod with Service Account token mounted automatically\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"automountServiceAccountToken set to true\"\n  pass: false\n  message: \"Pod with Service Account token mounted automatically\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          automountServiceAccountToken: true\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"automountServiceAccountToken set to false\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          automountServiceAccountToken: false\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/mitre/M-203_ssh_server.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-203\nslug: ssh-server\nseverity: Low\nmessage: \"SSH server running inside container\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\n    - group: \"\"\n      version: v1\n      resource: services\nparams:\n  sshPorts: [22, 2222]\nvalidations:\n  - expression: >\n      object.kind != 'Service' ||\n      !has(object.spec.ports) ||\n      object.spec.ports.all(p,\n        !(p.port in params.sshPorts) &&\n        !(p.?targetPort.orValue(0) in params.sshPorts)\n      )\n    message: \"Service should not be routing to SSH server\"\n\n  - expression: >\n      allContainers.all(container,\n        !has(container.ports) ||\n        container.ports.all(port,\n          !(port.containerPort in params.sshPorts)\n        )\n      )\n    message: \"Container could be running SSH server\"\n"
  },
  {
    "path": "internal/builtins/mitre/M-203_ssh_server_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"Service with no targetPort\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Service\n    metadata:\n      name: nginx\n    spec:\n      ports:\n        - port: 80\n      selector:\n        app: nginx\n      type: ClusterIP\n\n- name: \"Service with SSH port\"\n  pass: false\n  message: \"Service should not be routing to SSH server\"\n  input: |\n    apiVersion: v1\n    kind: Service\n    metadata:\n      name: nginx\n    spec:\n      ports:\n        - port: 22\n          targetPort: 80\n      selector:\n        app: nginx\n      type: ClusterIP\n\n- name: \"Service with SSH targetPort\"\n  pass: false\n  message: \"Service should not be routing to SSH server\"\n  input: |\n    apiVersion: v1\n    kind: Service\n    metadata:\n      name: nginx\n    spec:\n      ports:\n        - port: 8080\n          targetPort: 8080\n          name: http\n        - port: 80\n          targetPort: 2222\n          name: ssh\n      selector:\n        app: nginx\n      type: ClusterIP\n\n- name: \"Container with no ports\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container with empty ports\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              ports: []\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container with no SSH ports\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              ports:\n                - containerPort: 80\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container with SSH port\"\n  pass: false\n  message: \"Container could be running SSH server\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              ports:\n                - containerPort: 80\n                - containerPort: 22\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container with SSH port 2222\"\n  pass: false\n  message: \"Container could be running SSH server\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              ports:\n                - containerPort: 2222\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/nsa/M-300_read_only_root_filesystem.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: M-300\nslug: read-only-root-filesystem\nseverity: Low\nmessage: \"Root filesystem write allowed\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      allContainers.all(c, c.?securityContext.?readOnlyRootFilesystem.orValue(false) == true)\n    message: \"Container is able to write to the root filesystem\"\n"
  },
  {
    "path": "internal/builtins/nsa/M-300_read_only_root_filesystem_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: false\n  message: \"Container is able to write to the root filesystem\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.readOnlyRootFilesystem not specified\"\n  pass: false\n  message: \"Container is able to write to the root filesystem\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsNonRoot: true\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.readOnlyRootFilesystem set to false\"\n  pass: false\n  message: \"Container is able to write to the root filesystem\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                readOnlyRootFilesystem: false\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.readOnlyRootFilesystem set to true\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                readOnlyRootFilesystem: true\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-100_host_process.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_windowsHostProcess.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_windowsHostProcess_test.go\nid: M-100\nslug: host-process\nseverity: High\nmessage: \"Privileged access to the Windows node\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      podSpec.?securityContext.?windowsOptions.?hostProcess.orValue(false) == false\n    message: \"Pod with privileged access to the Windows node\"\n  - expression: >\n      allContainers.all(c, c.?securityContext.?windowsOptions.?hostProcess.orValue(false) == false)\n    message: \"Container with privileged access to the Windows node\"\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-100_host_process_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.windowsOptions not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsNonRoot: true\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.windowsOptions.hostProcess not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            windowsOptions:\n              runAsUserName: user\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.windowsOptions.hostProcess set to false\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            windowsOptions:\n              hostProcess: false\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.windowsOptions.hostProcess set to true\"\n  pass: false\n  message: \"Pod with privileged access to the Windows node\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          os:\n            name: windows\n          hostNetwork: true\n          securityContext:\n            windowsOptions:\n              hostProcess: true\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"second container with securityContext.windowsOptions.hostProcess set to true\"\n  pass: false\n  message: \"Container with privileged access to the Windows node\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          os:\n            name: windows\n          hostNetwork: true\n          containers:\n            - name: proxy\n              image: proxy\n              securityContext:\n                windowsOptions:\n                  hostProcess: true\n            - name: nginx\n              image: nginx\n              securityContext:\n                windowsOptions:\n                  hostProcess: true\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-101_host_namespaces.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_hostNamespaces.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_hostNamespaces_test.go\nid: M-101\nslug: host-namespaces\nseverity: High\nmessage: \"Host namespaces\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      podSpec.?hostNetwork.orValue(false) == false &&\n      podSpec.?hostPID.orValue(false) == false &&\n      podSpec.?hostIPC.orValue(false) == false\n    message: \"Pod sharing host namespace\"\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-101_host_namespaces_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"host options not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"hostNetwork set to true\"\n  pass: false\n  message: \"Pod sharing host namespace\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          hostNetwork: true\n          hostIPC: false\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"hostPID set to true\"\n  pass: false\n  message: \"Pod sharing host namespace\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          hostNetwork: false\n          hostPID: true\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"hostIPC set to true\"\n  pass: false\n  message: \"Pod sharing host namespace\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          hostIPC: true\n          hostPID: false\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"all host options set to false\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          hostNetwork: false\n          hostIPC: false\n          hostPID: false\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"all host options set to true\"\n  pass: false\n  message: \"Pod sharing host namespace\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          hostNetwork: true\n          hostIPC: true\n          hostPID: true\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-102_privileged_containers.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_privileged.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_privileged_test.go\nid: M-102\nslug: privileged-containers\nseverity: High\nmessage: \"Privileged container\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      allContainers.all(c, c.?securityContext.?privileged.orValue(false) == false)\n    message: \"Container running in privileged mode\"\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-102_privileged_containers_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.privileged not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: proxy\n              image: proxy\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsNonRoot: true\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.privileged set to true\"\n  pass: false\n  message: \"Container running in privileged mode\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: proxy\n              image: proxy\n            - name: nginx\n              image: nginx\n              securityContext:\n                privileged: true\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"initContainer securityContext.privileged set to true\"\n  pass: false\n  message: \"Container running in privileged mode\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          initContainers:\n            - name: init\n              image: busybox\n              securityContext:\n                privileged: true\n          containers:\n            - name: proxy\n              image: proxy\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-103_capabilities_baseline.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_capabilities_baseline.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_capabilities_baseline_test.go\nid: M-103\nslug: capabilities-baseline\nseverity: High\nmessage: \"Insecure capabilities\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nparams:\n  allowedCapabilities:\n    - AUDIT_WRITE\n    - CHOWN\n    - DAC_OVERRIDE\n    - FOWNER\n    - FSETID\n    - KILL\n    - MKNOD\n    - NET_BIND_SERVICE\n    - SETFCAP\n    - SETGID\n    - SETPCAP\n    - SETUID\n    - SYS_CHROOT\nvalidations:\n  - expression: >\n      allContainers.all(c, c.?securityContext.?capabilities.?add.orValue([]).all(cap, cap in params.allowedCapabilities))\n    message: \"Container running with not allowed capabilities\"\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-103_capabilities_baseline_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.capabilities not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsNonRoot: true\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.capabilities.add not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                capabilities:\n                  drop: [NET_BIND_SERVICE]\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.capabilities.add set with allowed capabilities\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                capabilities:\n                  add: [CHOWN, NET_BIND_SERVICE, SETGID, SETUID]\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.capabilities.add set only with forbidden capabilities\"\n  pass: false\n  message: \"Container running with not allowed capabilities\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                capabilities:\n                  add: [NET_ADMIN, SYS_TIME]\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.capabilities.add set with one forbidden capability\"\n  pass: false\n  message: \"Container running with not allowed capabilities\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                capabilities:\n                  add: [NET_BIND_SERVICE, NET_ADMIN]\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-104_host_path_volumes.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_hostPathVolumes.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_hostPathVolumes_test.go\nid: M-104\nslug: host-path-volumes\nseverity: High\nmessage: \"HostPath volume\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      podSpec.?volumes.orValue([]).all(v, !has(v.hostPath))\n    message: \"Pod with mounted host volume\"\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-104_host_path_volumes_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"volumes not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"hostPath volumes not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          volumes:\n            - name: config\n              configMap:\n                name: nginx-config\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"hostPath set to null\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          volumes:\n            - name: config\n              hostPath: null\n              configMap:\n                name: nginx-config\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"hostPath volume specified\"\n  pass: false\n  message: \"Pod with mounted host volume\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          volumes:\n            - name: cont1\n              hostPath:\n                path: path\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-105_host_ports.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_hostPorts.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_hostPorts_test.go\nid: M-105\nslug: host-ports\nseverity: High\nmessage: \"Not allowed hostPort\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nparams:\n  allowedHostPorts: [0]\nvalidations:\n  - expression: >\n      allContainers.all(c, c.?ports.orValue([]).all(p, p.?hostPort.orValue(0) in params.allowedHostPorts))\n    message: \"Container exposing not allowed port on the host\"\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-105_host_ports_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"ports not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"host ports not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              ports:\n                - containerPort: 80\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"host port set to 0\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              ports:\n                - containerPort: 80\n                  hostPort: 0\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"host ports specified\"\n  pass: false\n  message: \"Container exposing not allowed port on the host\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              ports:\n                - containerPort: 443\n                - containerPort: 80\n                  hostPort: 8080\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-106_apparmor.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_appArmorProfile.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_appArmorProfile_test.go\nid: M-106\nslug: apparmor\nseverity: Medium\nmessage: \"Forbidden AppArmor profile\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      !has(podMeta.annotations) ||\n      !podMeta.annotations.exists(key,\n        key.startsWith('container.apparmor.security.beta.kubernetes.io')\n      ) ||\n      podMeta.annotations.filter(key,\n        key.startsWith('container.apparmor.security.beta.kubernetes.io')\n      ).all(key,\n        podMeta.annotations[key] == 'runtime/default' ||\n        podMeta.annotations[key].startsWith('localhost/')\n      )\n    message: \"Container running with forbidden AppArmor profile\"\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-106_apparmor_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"annotations not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"AppArmor annotation not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n          annotations:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"AppArmor annotation set to unconfined\"\n  pass: false\n  message: \"Container running with forbidden AppArmor profile\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n          annotations:\n            container.apparmor.security.beta.kubernetes.io/nginx: unconfined\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"AppArmor annotation set to runtime/default\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n          annotations:\n            container.apparmor.security.beta.kubernetes.io/nginx: runtime/default\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"AppArmor annotation set to localhost/*\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n          annotations:\n            container.apparmor.security.beta.kubernetes.io/nginx: localhost/bar\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-107_selinux.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_seLinuxOptions.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_seLinuxOptions_test.go\nid: M-107\nslug: selinux\nseverity: Medium\nmessage: \"Forbidden SELinux options\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nparams:\n  allowedSELinuxTypes:\n    - container_t\n    - container_init_t\n    - container_kvm_t\n    - \"\"\nvalidations:\n  - expression: >\n      podSpec.?securityContext.?seLinuxOptions.?type.orValue(\"\") in params.allowedSELinuxTypes\n    message: \"Pod with not allowed SELinux type\"\n  - expression: >\n      allContainers.all(c, c.?securityContext.?seLinuxOptions.?type.orValue(\"\") in params.allowedSELinuxTypes)\n    message: \"Container with not allowed SELinux type\"\n  - expression: >\n      podSpec.?securityContext.?seLinuxOptions.?user.orValue(\"\") == \"\"\n    message: \"Pod with forbidden SELinux user\"\n  - expression: >\n      allContainers.all(c, c.?securityContext.?seLinuxOptions.?user.orValue(\"\") == \"\")\n    message: \"Container with forbidden SELinux user\"\n  - expression: >\n      podSpec.?securityContext.?seLinuxOptions.?role.orValue(\"\") == \"\"\n    message: \"Pod with forbidden SELinux role\"\n  - expression: >\n      allContainers.all(c, c.?securityContext.?seLinuxOptions.?role.orValue(\"\") == \"\")\n    message: \"Container with forbidden SELinux role\"\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-107_selinux_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.seLinuxOptions not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsNonRoot: true\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.seLinuxOptions.type not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seLinuxOptions:\n              level: \"s0:c123,c456\"\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.seLinuxOptions.type set to an empty string\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seLinuxOptions:\n              type: \"\"\n              level: \"s0:c123,c456\"\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.seLinuxOptions.type set to container_t\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seLinuxOptions:\n              type: container_t\n              level: \"s0:c123,c456\"\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.seLinuxOptions.type set to spc_t\"\n  pass: false\n  message: \"Pod with not allowed SELinux type\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seLinuxOptions:\n              type: spc_t\n              level: \"s0:c123,c456\"\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container securityContext.seLinuxOptions.type set to spc_t\"\n  pass: false\n  message: \"Container with not allowed SELinux type\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seLinuxOptions:\n              type: \"\"\n              level: \"s0:c123,c456\"\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                seLinuxOptions:\n                  type: spc_t\n                  level: \"s0:c123,c456\"\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container securityContext.seLinuxOptions.type set to container_init_t and Pod to spc_t\"\n  pass: false\n  message: \"Pod with not allowed SELinux type\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seLinuxOptions:\n              type: spc_t\n              level: \"s0:c123,c456\"\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                seLinuxOptions:\n                  type: container_init_t\n                  level: \"s0:c123,c456\"\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container securityContext.seLinuxOptions.user set to foo\"\n  pass: false\n  message: \"Container with forbidden SELinux user\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seLinuxOptions:\n              type: null\n              level: \"s0:c123,c456\"\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                seLinuxOptions:\n                  user: foo\n                  role: null\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Pod securityContext.seLinuxOptions.role set to bar\"\n  pass: false\n  message: \"Pod with forbidden SELinux role\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seLinuxOptions:\n              type: null\n              role: bar\n              user: ''\n              level: \"s0:c123,c456\"\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                seLinuxOptions:\n                  user: null\n                  role: null\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-108_proc_mount.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_procMount.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_procMount_test.go\nid: M-108\nslug: proc-mount\nseverity: Medium\nmessage: \"Forbidden proc mount type\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      allContainers.all(c, c.?securityContext.?procMount.orValue(\"Default\") == \"Default\")\n    message: \"Container using forbidden proc mount type\"\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-108_proc_mount_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n- name: \"securityContext.procMount not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsNonRoot: true\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.procMount set to Default\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                procMount: Default\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.procMount set to Default\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                procMount: Default\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.procMount set to Unmasked\"\n  pass: false\n  message: \"Container using forbidden proc mount type\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                procMount: Unmasked\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-109_seccomp_baseline.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_seccompProfile_baseline.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_seccompProfile_baseline_test.go\nid: M-109\nslug: seccomp-baseline\nseverity: Medium\nmessage: \"Forbidden seccomp profile\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      podSpec.?securityContext.?seccompProfile.?type.orValue(\"\") != \"Unconfined\"\n    message: \"Pod using forbidden seccomp profile\"\n  - expression: >\n      allContainers.all(c, c.?securityContext.?seccompProfile.?type.orValue(\"\") != \"Unconfined\")\n    message: \"Container using forbidden seccomp profile\"\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-109_seccomp_baseline_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.seccompProfile not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsNonRoot: true\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsNonRoot: true\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.seccompProfile.type set to RuntimeDefault\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seccompProfile:\n              type: RuntimeDefault\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.seccompProfile.type set to Localhost\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                seccompProfile:\n                  type: Localhost\n                  localhostProfile: local-profile\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container securityContext.seccompProfile.type set to Unconfined\"\n  pass: false\n  message: \"Container using forbidden seccomp profile\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                seccompProfile:\n                  type: Unconfined\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Pod securityContext.seccompProfile.type set to Unconfined\"\n  pass: false\n  message: \"Pod using forbidden seccomp profile\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seccompProfile:\n              type: Unconfined\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-110_sysctls.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#baseline\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_sysctls.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_sysctls_test.go\nid: M-110\nslug: sysctls\nseverity: Medium\nmessage: \"Unsafe sysctls\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nparams:\n  allowedSysctls:\n    - kernel.shm_rmid_forced\n    - net.ipv4.ip_local_port_range\n    - net.ipv4.ip_unprivileged_port_start\n    - net.ipv4.tcp_syncookies\n    - net.ipv4.ping_group_range\nvariables:\n  - name: sysctls\n    expression: podSpec.?securityContext.?sysctls.orValue([])\nvalidations:\n  - expression: >\n      variables.sysctls.size() == 0 || variables.sysctls.all(s,\n        s.name == null ||\n        s.name in params.allowedSysctls\n      )\n    message: \"Pod using unsafe sysctls\"\n"
  },
  {
    "path": "internal/builtins/pss/baseline/M-110_sysctls_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n- name: \"securityContext.sysctls not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsNonRoot: true\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.sysctls set to kernel.shm_rmid_forced\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            sysctls:\n              - name: kernel.shm_rmid_forced\n                value: \"0\"\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.sysctls set to net.ipv4.ip_unprivileged_port_start\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            sysctls:\n              - name: net.ipv4.ip_unprivileged_port_start\n                value: \"1000\"\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.sysctls set to kernel.msgmax\"\n  pass: false\n  message: \"Pod using unsafe sysctls\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            sysctls:\n              - name: kernel.msgmax\n                value: \"65536\"\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-111_volume_types.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_restrictedVolumes.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_hostPathVolumes_test.go\nid: M-111\nslug: volume-types\nseverity: Low\nmessage: \"Not allowed volume type\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nparams:\n  allowedVolumeTypes:\n    - configMap\n    - csi\n    - downwardAPI\n    - emptyDir\n    - ephemeral\n    - persistentVolumeClaim\n    - projected\n    - secret\nvariables:\n  - name: volumes\n    expression: podSpec.?volumes.orValue([])\nvalidations:\n  - expression: >\n      variables.volumes.size() == 0 || variables.volumes.all(v,\n        v.exists(key, key in params.allowedVolumeTypes)\n      )\n    message: \"Not allowed volume type used\"\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-111_volume_types_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"volumes not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"empty volume list\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          volumes: []\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"configMap volume\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          volumes:\n            - name: vol\n              configMap:\n                name: cm\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"nfs volume\"\n  pass: false\n  message: \"Not allowed volume type used\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          volumes:\n            - name: vol\n              nfs:\n                path: /path\n                server: server\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-112_privilege_escalation.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_allowPrivilegeEscalation.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_allowPrivilegeEscalation_test.go\nid: M-112\nslug: privilege-escalation\nseverity: Medium\nmessage: \"Allowed privilege escalation\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvariables:\n  - name: isWindows\n    expression: podSpec.?os.?name.orValue(\"\") == \"windows\"\nvalidations:\n  - expression: >\n      variables.isWindows || allContainers.all(c, c.?securityContext.?allowPrivilegeEscalation.orValue(true) == false)\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-112_privilege_escalation_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.allowPrivilegeEscalation not specified\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsNonRoot: true\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.allowPrivilegeEscalation set to false\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                allowPrivilegeEscalation: false\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.allowPrivilegeEscalation set to true\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                allowPrivilegeEscalation: true\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-113_run_as_non_root.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_runAsNonRoot.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_runAsNonRoot_test.go\nid: M-113\nslug: run-as-non-root\nseverity: Medium\nmessage: \"Container could be running as root user\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvariables:\n  # pod-level runAsNonRoot is explicitly set to true\n  - name: podRunAsNonRoot\n    expression: podSpec.?securityContext.?runAsNonRoot.orValue(false)\n\n  # pod-level runAsNonRoot is explicitly set to false\n  - name: podRunAsRoot\n    expression: podSpec.?securityContext.?runAsNonRoot.orValue(true) == false\n\n  # pod-level runAsUser is explicitly set to non-zero\n  - name: podRunAsNonZeroUser\n    expression: podSpec.?securityContext.?runAsUser.orValue(0) != 0\n\n  # containers that explicitly set runAsNonRoot=false\n  - name: explicitlyBadContainers\n    expression: >\n      allContainers.filter(c,\n          c.?securityContext.?runAsNonRoot.orValue(null) == false\n      )\n\n  # containers that\n  # - didn't set runAsNonRoot\n  # - aren't caught by a pod-level runAsNonRoot=true\n  # - didn't set non-zero runAsUser\n  # - aren't caught by a pod-level non-zero runAsUser\n  - name: implicitlyBadContainers\n    expression: >\n      allContainers.filter(c,\n          (!variables.podRunAsNonRoot && c.?securityContext.?runAsNonRoot.orValue(null) == null) &&\n          (!variables.podRunAsNonZeroUser && c.?securityContext.?runAsUser.orValue(0) == 0)\n      )\n\nvalidations:\n  - expression: >\n      !variables.podRunAsRoot && variables.explicitlyBadContainers.size() == 0 && variables.implicitlyBadContainers.size() == 0\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-113_run_as_non_root_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Pod set runAsNonRoot to false\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsNonRoot: false\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Pod set runAsNonRoot to false and container to true\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsNonRoot: false\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsNonRoot: true\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"container set runAsNonRoot to false and Pod to true\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsNonRoot: true\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsNonRoot: false\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"container set runAsNonRoot to true and Pod not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsNonRoot: true\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Pod set runAsNonRoot to true and container not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsNonRoot: true\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Pod set runAsUser to non-zero\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      securityContext:\n        runAsUser: 1\n      containers:\n        - name: nginx\n          image: nginx\n\n- name: \"container set runAsUser to non-zero\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      containers:\n        - name: nginx\n          image: nginx\n          securityContext:\n            runAsUser: 1\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-114_run_as_user.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_runAsUser.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_runAsUser_test.go\nid: M-114\nslug: run-as-user\nseverity: Medium\nmessage: \"Container running as root UID\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvalidations:\n  - expression: >\n      podSpec.?securityContext.?runAsUser.orValue(-1) != 0 &&\n      allContainers.all(c, c.?securityContext.?runAsUser.orValue(-1) != 0)\n    message: \"Container running as root UID\"\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-114_run_as_user_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Pod explicitly set runAsUser to 0 and Container to 1000\"\n  pass: false\n  message: \"Container running as root UID\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsUser: 0\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsUser: 1000\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container explicitly set runAsUser to 0 and Pod to 1000\"\n  pass: false\n  message: \"Container running as root UID\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsUser: 1000\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsUser: 0\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container set runAsUser to 1000 and Pod not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsUser: 1000\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Pod set runAsUser to 1000 and Container not specified\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsUser: 1000\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-115_seccomp_restricted.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_seccompProfile_restricted.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_seccompProfile_restricted_test.go\nid: M-115\nslug: seccomp-restricted\nseverity: Low\nmessage: \"Not allowed seccomp profile\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nvariables:\n  # pod-level seccompProfile is explicitly different to Unconfined\n  - name: podSeccompSet\n    expression: podSpec.?securityContext.?seccompProfile.?type.orValue(\"Unconfined\") != \"Unconfined\"\n\n  # pod-level seccompProfile is explicitly Unconfined\n  - name: podSeccompUnconfined\n    expression: podSpec.?securityContext.?seccompProfile.?type.orValue(\"\") == \"Unconfined\"\n\n  # containers that explicitly set seccompProfile.type to Unconfined\n  - name: explicitlyBadContainers\n    expression: allContainers.filter(c, c.?securityContext.?seccompProfile.?type.orValue(\"\") == \"Unconfined\")\n\n  # containers that didn't set seccompProfile and aren't caught by a pod-level seccompProfile\n  - name: implicitlyBadContainers\n    expression: >\n      allContainers.filter(c, \n        !variables.podSeccompSet && c.?securityContext.?seccompProfile.orValue(null) == null\n      )\n\n  - name: isWindows\n    expression: podSpec.?os.?name.orValue(\"\") == \"windows\"\nvalidations:\n  - expression: >\n      variables.isWindows || \n      ( \n        !variables.podSeccompUnconfined && \n        variables.explicitlyBadContainers.size() == 0 && \n        variables.implicitlyBadContainers.size() == 0\n      )\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-115_seccomp_restricted_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.seccompProfile not specified\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            runAsNonRoot: true\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsNonRoot: true\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.seccompProfile.type set to RuntimeDefault\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seccompProfile:\n              type: RuntimeDefault\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.seccompProfile.type set to Localhost\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                seccompProfile:\n                  type: Localhost\n                  localhostProfile: local-profile\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Container securityContext.seccompProfile.type set to Unconfined\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seccompProfile:\n              type: RuntimeDefault\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                seccompProfile:\n                  type: Unconfined\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"Pod securityContext.seccompProfile.type set to Unconfined\"\n  pass: false\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          securityContext:\n            seccompProfile:\n              type: Unconfined\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                seccompProfile:\n                  type: Localhost\n                  localhostProfile: local-profile\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-116_capabilities_restricted.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n# https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_capabilities_restricted.go\n# https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/pod-security-admission/policy/check_capabilities_restricted_test.go\nid: M-116\nslug: capabilities-restricted\nseverity: Low\nmessage: \"Not allowed added/dropped capabilities\"\nmatch:\n  resources:\n    - group: \"\"\n      version: v1\n      resource: pods\n    - group: apps\n      version: v1\n      resource: deployments\n    - group: apps\n      version: v1\n      resource: daemonsets\n    - group: apps\n      version: v1\n      resource: statefulsets\n    - group: apps\n      version: v1\n      resource: replicasets\n    - group: batch\n      version: v1\n      resource: cronjobs\n    - group: batch\n      version: v1\n      resource: jobs\nparams:\n  allowedCapabilities:\n    - NET_BIND_SERVICE\nvariables:\n  - name: isWindows\n    expression: podSpec.?os.?name.orValue(\"\") == \"windows\"\nvalidations:\n  - expression: >\n      variables.isWindows || allContainers.all(c, c.?securityContext.?capabilities.?drop.orValue([]).exists(ca, ca == 'ALL'))\n    message: \"Containers must drop ALL capabilities\"\n  - expression: >\n      variables.isWindows || allContainers.all(c, c.?securityContext.?capabilities.?add.orValue([]).all(ca, ca in params.allowedCapabilities))\n    message: \"Container running with not allowed capabilities\"\n"
  },
  {
    "path": "internal/builtins/pss/restricted/M-116_capabilities_restricted_test.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: \"securityContext not specified\"\n  pass: false\n  message: \"Containers must drop ALL capabilities\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.capabilities not specified\"\n  pass: false\n  message: \"Containers must drop ALL capabilities\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                runAsNonRoot: true\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.capabilities.drop not includes ALL\"\n  pass: false\n  message: \"Containers must drop ALL capabilities\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                capabilities:\n                  drop: [KILL]\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.capabilities.drop includes ALL\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                capabilities:\n                  drop: [KILL, ALL, CHOWN]\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.capabilities.add includes not allowed capabilities\"\n  pass: false\n  message: \"Container running with not allowed capabilities\"\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                capabilities:\n                  drop: [ALL]\n                  add: [CHOWN, NET_BIND_SERVICE, SETGID, SETUID]\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"securityContext.capabilities.add only NET_BIND_SERVICE\"\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n              securityContext:\n                capabilities:\n                  drop: [ALL]\n                  add: [NET_BIND_SERVICE]\n      selector:\n        matchLabels:\n          app: nginx\n\n- name: \"windows\"\n  pass: true\n  input: |\n    apiVersion: v1\n    kind: Pod\n    metadata:\n      name: nginx\n      labels:\n        app: nginx\n    spec:\n      os:\n        name: windows\n      containers:\n        - name: nginx\n          image: nginx\n          securityContext: {}\n"
  },
  {
    "path": "main.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage main\n\nimport \"github.com/undistro/marvin/cmd\"\n\nfunc main() {\n\tcmd.Execute()\n}\n"
  },
  {
    "path": "pkg/cmd/scan.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage cmd\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/go-logr/logr\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/version\"\n\t\"k8s.io/cli-runtime/pkg/genericclioptions\"\n\t\"k8s.io/client-go/dynamic\"\n\t_ \"k8s.io/client-go/plugin/pkg/client/auth\"\n\t\"k8s.io/utils/pointer\"\n\n\t\"github.com/spf13/pflag\"\n\n\t\"github.com/undistro/marvin/pkg/loader\"\n\t\"github.com/undistro/marvin/pkg/printers\"\n\t\"github.com/undistro/marvin/pkg/types\"\n\t\"github.com/undistro/marvin/pkg/validator\"\n)\n\ntype ScanOptions struct {\n\t*genericclioptions.ConfigFlags\n\tgenericclioptions.IOStreams\n\n\tChecksPath            *string\n\tDisableBuiltIn        *bool\n\tOutputFormat          *string\n\tNoFail                *bool\n\tSkipAnnotation        *string\n\tDisableAnnotationSkip *bool\n\tCostLimit             *uint64\n\n\tctx          context.Context\n\tlog          logr.Logger\n\tprinter      printers.Printer\n\tclient       *dynamic.DynamicClient\n\tkubeVersion  *version.Info\n\tapiResources []*metav1.APIResourceList\n\tresources    map[string][]unstructured.Unstructured\n\tgvrs         map[string]string\n}\n\n// NewScanOptions returns a ScanOptions with the default values\nfunc NewScanOptions() *ScanOptions {\n\treturn &ScanOptions{\n\t\tConfigFlags:           genericclioptions.NewConfigFlags(false),\n\t\tIOStreams:             genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr},\n\t\tChecksPath:            pointer.String(\"\"),\n\t\tDisableBuiltIn:        pointer.Bool(false),\n\t\tOutputFormat:          pointer.String(\"table\"),\n\t\tNoFail:                pointer.Bool(false),\n\t\tDisableAnnotationSkip: pointer.Bool(false),\n\t\tSkipAnnotation:        pointer.String(\"marvin.undistro.io/skip\"),\n\t\tCostLimit:             pointer.Uint64(1000000),\n\t}\n}\n\n// AddFlags binds scan configuration flags to a given flagset\nfunc (o *ScanOptions) AddFlags(flags *pflag.FlagSet) {\n\to.ConfigFlags.AddFlags(flags)\n\tif o.ChecksPath != nil {\n\t\tflags.StringVarP(o.ChecksPath, \"checks\", \"f\", *o.ChecksPath, \"Path to the check files directory\")\n\t}\n\tif o.DisableBuiltIn != nil {\n\t\tflags.BoolVar(o.DisableBuiltIn, \"disable-builtin\", *o.DisableBuiltIn, \"Disable builtin checks\")\n\t}\n\tif o.OutputFormat != nil {\n\t\tflags.StringVarP(o.OutputFormat, \"output\", \"o\", *o.OutputFormat, `Output format. One of: (\"table\", \"json\", \"yaml\" or \"markdown\")`)\n\t}\n\tif o.NoFail != nil {\n\t\tflags.BoolVar(o.NoFail, \"no-fail\", *o.NoFail, \"Return an exit code of zero even if there are errors in the report\")\n\t}\n\tif o.SkipAnnotation != nil {\n\t\tflags.StringVar(o.SkipAnnotation, \"skip-annotation\", *o.SkipAnnotation, \"Annotation name for skipping checks\")\n\t}\n\tif o.DisableAnnotationSkip != nil {\n\t\tflags.BoolVar(o.DisableAnnotationSkip, \"disable-annotation-skip\", *o.DisableAnnotationSkip, \"Disable resource skipping by annotation\")\n\t}\n\tif o.CostLimit != nil {\n\t\tflags.Uint64Var(o.CostLimit, \"cost-limit\", *o.CostLimit, \"CEL cost limit. Set 0 to disable it.\")\n\t}\n}\n\n// Init initializes the kubernetes clients, get server version and API resources\nfunc (o *ScanOptions) Init(ctx context.Context) error {\n\tif err := o.Validate(); err != nil {\n\t\treturn err\n\t}\n\to.ctx = ctx\n\to.log = logr.FromContextOrDiscard(o.ctx)\n\n\tvar printer printers.Printer\n\tswitch *o.OutputFormat {\n\tcase \"json\":\n\t\tprinter = &printers.JSONPrinter{}\n\tcase \"yaml\":\n\t\tprinter = &printers.YAMLPrinter{}\n\tcase \"table\":\n\t\tprinter = &printers.TablePrinter{}\n\tcase \"markdown\":\n\t\tcolor.NoColor = true\n\t\tprinter = &printers.MarkdownPrinter{}\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid output format '%s'\", *o.OutputFormat)\n\t}\n\n\tdynamicClient, err := o.ToDynamicClient()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"dynamic client error: %s\", err.Error())\n\t}\n\tdiscoveryClient, err := o.ToDiscoveryClient()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"kubernetes client error: %s\", err.Error())\n\t}\n\tkubeVersion, err := discoveryClient.ServerVersion()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"server version error: %s\", err.Error())\n\t}\n\t_, apiResources, err := discoveryClient.ServerGroupsAndResources()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"server groups error: %s\", err.Error())\n\t}\n\n\to.client = dynamicClient\n\to.kubeVersion = kubeVersion\n\to.apiResources = apiResources\n\to.printer = printer\n\to.resources = make(map[string][]unstructured.Unstructured)\n\to.gvrs = make(map[string]string)\n\treturn nil\n}\n\n// Validate ensures that all required arguments and flag values are provided\nfunc (o *ScanOptions) Validate() error {\n\tif *o.DisableBuiltIn == true && *o.ChecksPath == \"\" {\n\t\treturn errors.New(`please set '--checks/-f' or keep 'disable-builtin' 'false'`)\n\t}\n\treturn nil\n}\n\n// ToDynamicClient returns a DynamicClient using a computed RESTConfig.\nfunc (o *ScanOptions) ToDynamicClient() (*dynamic.DynamicClient, error) {\n\tconfig, err := o.ToRESTConfig()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn dynamic.NewForConfig(config)\n}\n\n// Run executes the scan command\nfunc (o *ScanOptions) Run() (bool, error) {\n\tallChecks, err := o.getChecks()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\treport := types.NewReport(o.kubeVersion)\n\tfor _, check := range allChecks {\n\t\tcr := o.runCheck(check)\n\t\treport.Add(cr)\n\t}\n\n\treport.GVRs = o.gvrs\n\thasError := report.HasError()\n\tif hasError {\n\t\to.log.Info(\"scan finished with errors\")\n\t}\n\treturn hasError, o.printer.PrintObj(*report, o.Out)\n}\n\n// getChecks returns a list of checks.Check based on the flags, including built-in checks or/and from a path.\nfunc (o *ScanOptions) getChecks() ([]types.Check, error) {\n\to.log.V(3).Info(\"loading checks\", \"builtin\", !*o.DisableBuiltIn, \"custom\", *o.ChecksPath != \"\")\n\tvar allChecks []types.Check\n\tif !*o.DisableBuiltIn {\n\t\tallChecks = loader.Builtins\n\t\to.log.V(3).Info(\"builtin checks loaded\", \"total\", len(loader.Builtins))\n\t}\n\tif *o.ChecksPath != \"\" {\n\t\tlocalChecks, err := loader.LoadChecks(*o.ChecksPath)\n\t\tif err != nil {\n\t\t\to.log.Error(err, \"failed to load checks\", \"path\", *o.ChecksPath)\n\t\t\treturn nil, fmt.Errorf(\"load checks error: %s\", err.Error())\n\t\t}\n\t\to.log.V(2).Info(\"custom checks loaded\", \"total\", len(localChecks))\n\t\tallChecks = append(allChecks, localChecks...)\n\t}\n\treturn allChecks, nil\n}\n\n// runCheck performs a check\nfunc (o *ScanOptions) runCheck(check types.Check) *types.CheckResult {\n\tlog := o.log.WithValues(\"check\", check.ID)\n\tcr := types.NewCheckResult(check)\n\tdefer cr.UpdateStatus()\n\tv, err := validator.Compile(check, o.apiResources, o.kubeVersion, *o.CostLimit)\n\tif err != nil {\n\t\tlog.Error(err, \"failed to compile check \"+check.ID)\n\t\tcr.AddError(fmt.Errorf(\"%s compile error: %s\", check.Path, err.Error()))\n\t\treturn cr\n\t}\n\tlog.V(3).Info(\"check compiled successfully\")\n\tresources, errs := o.loadResources(check)\n\tcr.AddErrors(errs...)\n\tfor gvr, objs := range resources {\n\t\tfor _, obj := range objs {\n\t\t\tlog := log.WithValues(\"obj\", fmt.Sprintf(\"%s/%s\", types.GVK(obj), types.NamespacedName(obj)))\n\t\t\to.addGVR(obj, gvr)\n\t\t\tif o.isSkipped(check.ID, obj.GetAnnotations()) {\n\t\t\t\tlog.V(4).Info(\"skipped\")\n\t\t\t\tcr.AddSkipped(obj)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tpassed, _, err := v.Validate(obj, check.Params)\n\t\t\tif err != nil {\n\t\t\t\tlog.Error(err, \"failed to validate check \"+check.ID)\n\t\t\t\tcr.AddError(fmt.Errorf(\"%s validate error: %s\", check.Path, err.Error()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif passed {\n\t\t\t\tlog.V(4).Info(\"passed\")\n\t\t\t\tcr.AddPassed(obj)\n\t\t\t} else {\n\t\t\t\tlog.V(4).Info(\"failed\")\n\t\t\t\tcr.AddFailed(obj)\n\t\t\t}\n\t\t}\n\t}\n\treturn cr\n}\n\n// loadResources returns a map of resource slice by GVR to be validated by the given check\nfunc (o *ScanOptions) loadResources(check types.Check) (map[string][]unstructured.Unstructured, []error) {\n\tresources := map[string][]unstructured.Unstructured{}\n\tvar errs []error\n\tfor _, r := range check.Match.Resources {\n\t\tgvr := r.ToGVR()\n\t\tgvrs := fmt.Sprintf(\"%s/%s\", gvr.GroupVersion().String(), gvr.Resource)\n\t\tlog := o.log.WithValues(\"check\", check.ID)\n\t\tobjs, cached := o.resources[gvrs]\n\t\tif cached {\n\t\t\tlog.V(3).Info(gvrs+\" resources cached\", \"total\", len(objs))\n\t\t\tresources[gvrs] = objs\n\t\t} else {\n\t\t\tlog.V(3).Info(fmt.Sprintf(\"listing %s from kubernetes\", gvrs))\n\t\t\tul, err := o.client.Resource(gvr).Namespace(*o.Namespace).List(o.ctx, metav1.ListOptions{})\n\t\t\tif err != nil {\n\t\t\t\tlog.Error(err, \"failed to list \"+gvrs)\n\t\t\t\terrs = append(errs, fmt.Errorf(\"list %s error: %s\", gvr.Resource, err.Error()))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tlog.V(1).Info(gvrs+\" loaded from kubernetes\", \"total\", len(ul.Items))\n\t\t\to.resources[gvrs] = ul.Items\n\t\t\tresources[gvrs] = ul.Items\n\t\t}\n\t}\n\treturn resources, errs\n}\n\n// isSkipped returns true if the checkID is annotated to be skipped\nfunc (o *ScanOptions) isSkipped(checkID string, annotations map[string]string) bool {\n\tif annotations == nil {\n\t\treturn false\n\t}\n\tif *o.DisableAnnotationSkip {\n\t\treturn false\n\t}\n\tv, ok := annotations[*o.SkipAnnotation]\n\tif !ok {\n\t\treturn false\n\t}\n\tids := strings.Split(v, \",\")\n\tfor _, s := range ids {\n\t\tif strings.TrimSpace(s) == checkID {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// addGVR updates the map of GVR by GVK\nfunc (o *ScanOptions) addGVR(obj unstructured.Unstructured, gvr string) {\n\tgvk := types.GVK(obj)\n\tif _, ok := o.gvrs[gvk]; !ok {\n\t\to.gvrs[gvk] = gvr\n\t}\n}\n"
  },
  {
    "path": "pkg/loader/builtin.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage loader\n\nimport (\n\t\"io/fs\"\n\t\"log\"\n\n\t\"github.com/undistro/marvin/internal/builtins\"\n\t\"github.com/undistro/marvin/pkg/types\"\n)\n\n// Builtins represents the builtins checks\nvar Builtins []types.Check\n\nfunc init() {\n\tc, _, walkFn := walkDir(builtins.EmbedChecksFS.ReadFile, true)\n\terr := fs.WalkDir(builtins.EmbedChecksFS, \".\", walkFn)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tBuiltins = c.toList()\n}\n"
  },
  {
    "path": "pkg/loader/builtin_test.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage loader\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestBuiltins(t *testing.T) {\n\tassert.NotNil(t, Builtins)\n\tassert.Greater(t, len(Builtins), 0)\n\tassert.Equal(t, 35, len(Builtins))\n}\n"
  },
  {
    "path": "pkg/loader/loader.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage loader\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/fs\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"sigs.k8s.io/yaml\"\n\n\t\"github.com/undistro/marvin/pkg/types\"\n)\n\ntype (\n\tChecksMap    map[string]types.Check\n\tTestsMap     map[string][]types.Test\n\treadFileFunc func(string) ([]byte, error)\n)\n\n// toList returns a slice of Check\nfunc (cm ChecksMap) toList() []types.Check {\n\tif cm == nil {\n\t\treturn nil\n\t}\n\tlist := make([]types.Check, 0, len(cm))\n\tfor _, c := range cm {\n\t\tlist = append(list, c)\n\t}\n\treturn list\n}\n\n// supportedExt supported file extensions for checks and tests\nvar supportedExt = map[string]bool{\n\t\".yaml\": true,\n\t\".yml\":  true,\n\t\".json\": true,\n}\n\n// LoadChecks loads all checks from given path recursively\nfunc LoadChecks(root string) ([]types.Check, error) {\n\tc, _, err := load(root)\n\treturn c.toList(), err\n}\n\n// LoadChecksAndTests loads all checks and their tests from given path recursively\nfunc LoadChecksAndTests(root string) (ChecksMap, TestsMap, error) {\n\treturn load(root)\n}\n\nfunc load(root string) (ChecksMap, TestsMap, error) {\n\tcheck, tests, walkFn := walkDir(os.ReadFile, false)\n\terr := filepath.WalkDir(root, walkFn)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn check, tests, nil\n}\n\nfunc walkDir(readFileFn readFileFunc, builtin bool) (ChecksMap, TestsMap, fs.WalkDirFunc) {\n\ttests := make(TestsMap)\n\tcheck := make(ChecksMap)\n\treturn check, tests, func(path string, d fs.DirEntry, err error) error {\n\t\tif err != nil || d.IsDir() {\n\t\t\treturn err\n\t\t}\n\t\text := filepath.Ext(path)\n\t\tif !supportedExt[ext] {\n\t\t\treturn nil // unsupported file\n\t\t}\n\t\tbs, err := readFileFn(path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\ttestSuffix := \"_test\" + ext\n\t\tisTest := strings.HasSuffix(path, testSuffix)\n\t\tif isTest {\n\t\t\tt, err := parseTests(ext, bs)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tk := strings.TrimSuffix(path, testSuffix)\n\t\t\ttests[k] = t\n\t\t\treturn nil\n\t\t}\n\t\tc, err := parseCheck(ext, bs)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tc.Builtin = builtin\n\t\tc.Path = path\n\t\tk := strings.TrimSuffix(path, ext)\n\t\tif builtin {\n\t\t\tk = \"builtin:\" + k\n\t\t}\n\t\tcheck[k] = c\n\t\treturn nil\n\t}\n}\n\nfunc parseCheck(ext string, bs []byte) (types.Check, error) {\n\tobj := types.Check{}\n\treturn parse(ext, bs, obj)\n}\n\nfunc parseTests(ext string, bs []byte) ([]types.Test, error) {\n\tvar obj []types.Test\n\treturn parse(ext, bs, obj)\n}\n\nfunc parse[T any](ext string, bs []byte, obj T) (T, error) {\n\tvar err error\n\tswitch ext {\n\tcase \".yaml\", \".yml\":\n\t\terr = yaml.Unmarshal(bs, &obj)\n\tcase \".json\":\n\t\terr = json.Unmarshal(bs, &obj)\n\tdefault:\n\t\treturn obj, fmt.Errorf(\"unsupported file extension: %s\", ext)\n\t}\n\treturn obj, err\n}\n"
  },
  {
    "path": "pkg/loader/loader_test.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage loader\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLoadChecks(t *testing.T) {\n\temptyDir := t.TempDir()\n\tassert.NotEmpty(t, emptyDir)\n\n\ttests := []struct {\n\t\tname    string\n\t\tpath    string\n\t\twantLen int\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname:    \"recursive\",\n\t\t\tpath:    \"testdata/checks/\",\n\t\t\twantLen: 2,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"depth 1\",\n\t\t\tpath:    \"testdata/checks/workloads/\",\n\t\t\twantLen: 1,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"valid file\",\n\t\t\tpath:    \"testdata/checks/workloads/replicas.yaml\",\n\t\t\twantLen: 1,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid file\",\n\t\t\tpath:    \"testdata/checks/workloads/unsupported.txt\",\n\t\t\twantLen: 0,\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname:    \"not found\",\n\t\t\tpath:    \"testdata/checks/notfound\",\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname:    \"empty\",\n\t\t\tpath:    emptyDir,\n\t\t\twantErr: false,\n\t\t\twantLen: 0,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid YAML\",\n\t\t\tpath:    \"testdata/invalid/\",\n\t\t\twantErr: true,\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 := LoadChecks(tt.path)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"LoadChecks() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(got) != tt.wantLen {\n\t\t\t\tt.Errorf(\"LoadChecks() got = %v items, want %v\", len(got), tt.wantLen)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestLoadChecksAndTests(t *testing.T) {\n\temptyDir := t.TempDir()\n\tassert.NotEmpty(t, emptyDir)\n\n\ttests := []struct {\n\t\tname          string\n\t\tpath          string\n\t\twantChecksLen int\n\t\twantTestsLen  int\n\t\twantErr       bool\n\t}{\n\t\t{\n\t\t\tname:          \"recursive\",\n\t\t\tpath:          \"testdata/checks/\",\n\t\t\twantChecksLen: 2,\n\t\t\twantTestsLen:  1,\n\t\t\twantErr:       false,\n\t\t},\n\t\t{\n\t\t\tname:          \"depth 1\",\n\t\t\tpath:          \"testdata/checks/workloads/\",\n\t\t\twantChecksLen: 1,\n\t\t\twantTestsLen:  1,\n\t\t\twantErr:       false,\n\t\t},\n\t\t{\n\t\t\tname:          \"valid file\",\n\t\t\tpath:          \"testdata/checks/workloads/replicas.yaml\",\n\t\t\twantChecksLen: 1,\n\t\t\twantErr:       false,\n\t\t},\n\t\t{\n\t\t\tname:          \"invalid file\",\n\t\t\tpath:          \"testdata/checks/workloads/unsupported.txt\",\n\t\t\twantChecksLen: 0,\n\t\t\twantErr:       false,\n\t\t},\n\t\t{\n\t\t\tname:    \"not found\",\n\t\t\tpath:    \"testdata/checks/notfound\",\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname:          \"empty\",\n\t\t\tpath:          emptyDir,\n\t\t\twantErr:       false,\n\t\t\twantChecksLen: 0,\n\t\t},\n\t\t{\n\t\t\tname:    \"invalid YAML\",\n\t\t\tpath:    \"testdata/invalid/\",\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgotChecks, gotTests, err := LoadChecksAndTests(tt.path)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"LoadChecksAndTests() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(gotChecks) != tt.wantChecksLen {\n\t\t\t\tt.Errorf(\"LoadChecksAndTests() got = %v items, want %v\", len(gotChecks), tt.wantChecksLen)\n\t\t\t}\n\t\t\tif len(gotTests) != tt.wantTestsLen {\n\t\t\t\tt.Errorf(\"LoadChecksAndTests() got = %v items, want %v\", len(gotTests), tt.wantTestsLen)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/loader/testdata/checks/svc_lb.json",
    "content": "{\n  \"id\": \"TEST-002\",\n  \"message\": \"Type Loadbalancer detected. Could be expensive\",\n  \"severity\": \"Low\",\n  \"match\": {\n    \"resources\": [\n      {\n        \"group\": \"\",\n        \"version\": \"v1\",\n        \"resource\": \"services\"\n      }\n    ]\n  },\n  \"validations\": [\n    {\n      \"expression\": \"object.spec.type != 'LoadBalancer'\",\n      \"message\": \"Type Loadbalancer detected. Could be expensive\"\n    }\n  ]\n}\n"
  },
  {
    "path": "pkg/loader/testdata/checks/workloads/replicas.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\nid: TEST-001\nseverity: Low\nmessage: \"minimum quantity of replicas (2) not reached\"\nmatch:\n  resources:\n    - group: apps\n      version: v1\n      resource: deployments\nvalidations:\n  - expression: \"object.spec.replicas >= 2\"\n    message: \"minimum quantity of replicas (2) not reached\"\n"
  },
  {
    "path": "pkg/loader/testdata/checks/workloads/replicas_test.yaml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\n- name: 'replicas set to 1'\n  pass: false\n  message: 'minimum quantity of replicas (2) not reached'\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      replicas: 1\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n- name: 'not specified'\n  pass: false\n  message: 'minimum quantity of replicas (2) not reached'\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n- name: 'replicas set to 2'\n  pass: true\n  input: |\n    apiVersion: apps/v1\n    kind: Deployment\n    metadata:\n      name: nginx\n    spec:\n      replicas: 2\n      template:\n        metadata:\n          name: nginx\n          labels:\n            app: nginx\n        spec:\n          containers:\n            - name: nginx\n              image: nginx\n      selector:\n        matchLabels:\n          app: nginx\n"
  },
  {
    "path": "pkg/loader/testdata/checks/workloads/unsupported.txt",
    "content": "unsupported file type"
  },
  {
    "path": "pkg/loader/testdata/invalid/invalid.yml",
    "content": "# Copyright 2023 Undistro Authors\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.\n\ninvalid YAML"
  },
  {
    "path": "pkg/printers/interface.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage printers\n\nimport (\n\t\"io\"\n\n\t\"github.com/undistro/marvin/pkg/types\"\n)\n\n// Printer is an interface that defines a method for printing a Report to an io.Writer\ntype Printer interface {\n\tPrintObj(types.Report, io.Writer) error\n}\n"
  },
  {
    "path": "pkg/printers/json.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage printers\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\n\t\"github.com/undistro/marvin/pkg/types\"\n)\n\n// JSONPrinter implements a Printer that prints the report in JSON format\ntype JSONPrinter struct{}\n\nfunc (*JSONPrinter) PrintObj(report types.Report, w io.Writer) error {\n\tdata, err := json.Marshal(report)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdata = append(data, '\\n')\n\t_, err = w.Write(data)\n\treturn err\n}\n"
  },
  {
    "path": "pkg/printers/md.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage printers\n\nimport (\n\t\"io\"\n\n\t\"github.com/olekukonko/tablewriter\"\n\n\t\"github.com/undistro/marvin/pkg/types\"\n)\n\n// MarkdownPrinter implements a Printer that prints the report in Markdown format\ntype MarkdownPrinter struct{}\n\nfunc (*MarkdownPrinter) PrintObj(report types.Report, w io.Writer) error {\n\tt := tablewriter.NewWriter(w)\n\tt.SetAutoWrapText(false)\n\tt.SetHeaderAlignment(tablewriter.ALIGN_LEFT)\n\tt.SetAlignment(tablewriter.ALIGN_LEFT)\n\tt.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})\n\tt.SetCenterSeparator(\"|\")\n\n\trenderTable(report, t)\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/printers/table.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage printers\n\nimport (\n\t\"io\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/fatih/color\"\n\t\"github.com/olekukonko/tablewriter\"\n\n\t\"github.com/undistro/marvin/pkg/types\"\n)\n\nvar (\n\tred     = color.New(color.FgRed).SprintfFunc()\n\tredBold = color.New(color.FgRed, color.Bold).SprintfFunc()\n\tyellow  = color.New(color.FgYellow).SprintfFunc()\n\tblue    = color.New(color.FgBlue).SprintfFunc()\n\tgreen   = color.New(color.FgGreen).SprintfFunc()\n)\n\n// TablePrinter implements a Printer that prints the report in table format\ntype TablePrinter struct {\n}\n\nfunc (r *TablePrinter) PrintObj(report types.Report, w io.Writer) error {\n\tt := tablewriter.NewWriter(w)\n\tt.SetAutoWrapText(false)\n\tt.SetAutoFormatHeaders(true)\n\tt.SetHeaderAlignment(tablewriter.ALIGN_LEFT)\n\tt.SetAlignment(tablewriter.ALIGN_LEFT)\n\tt.SetCenterSeparator(\"\")\n\tt.SetColumnSeparator(\"\")\n\tt.SetRowSeparator(\"\")\n\tt.SetHeaderLine(false)\n\tt.SetBorder(false)\n\tt.SetTablePadding(\"   \")\n\tt.SetNoWhiteSpace(true)\n\n\trenderTable(report, t)\n\treturn nil\n}\n\nfunc renderTable(report types.Report, t *tablewriter.Table) {\n\tt.SetHeader([]string{\"SEVERITY\", \"ID\", \"CHECK\", \"STATUS\", \"FAILED\", \"PASSED\", \"SKIPPED\"})\n\tsort.Slice(report.Checks, func(i, j int) bool {\n\t\tif report.Checks[i].Severity != report.Checks[j].Severity {\n\t\t\treturn report.Checks[i].Severity > report.Checks[j].Severity\n\t\t}\n\t\tif report.Checks[i].TotalFailed != report.Checks[j].TotalFailed {\n\t\t\treturn report.Checks[i].TotalFailed > report.Checks[j].TotalFailed\n\t\t}\n\t\tif report.Checks[i].TotalPassed != report.Checks[j].TotalPassed {\n\t\t\treturn report.Checks[i].TotalPassed > report.Checks[j].TotalPassed\n\t\t}\n\t\treturn strings.Compare(report.Checks[i].ID, report.Checks[j].ID) < 0\n\t})\n\tfor _, c := range report.Checks {\n\t\tt.Append([]string{\n\t\t\tcolorSeverity(c.Severity),\n\t\t\tc.ID,\n\t\t\tc.Message,\n\t\t\tcolorStatus(c.Status),\n\t\t\tstrconv.Itoa(c.TotalFailed),\n\t\t\tstrconv.Itoa(c.TotalPassed),\n\t\t\tstrconv.Itoa(c.TotalSkipped),\n\t\t})\n\t}\n\tt.Render()\n}\n\nfunc colorSeverity(s types.Severity) string {\n\tswitch s {\n\tcase types.SeverityLow:\n\t\treturn blue(\"%s\", s)\n\tcase types.SeverityMedium:\n\t\treturn yellow(\"%s\", s)\n\tcase types.SeverityHigh:\n\t\treturn red(\"%s\", s)\n\tcase types.SeverityCritical:\n\t\treturn redBold(\"%s\", s)\n\tdefault:\n\t\treturn s.String()\n\t}\n}\nfunc colorStatus(s types.CheckStatus) string {\n\tswitch s {\n\tcase types.StatusPassed:\n\t\treturn green(\"%s\", s)\n\tcase types.StatusSkipped:\n\t\treturn blue(\"%s\", s)\n\tcase types.StatusFailed:\n\t\treturn red(\"%s\", s)\n\tcase types.StatusError:\n\t\treturn redBold(\"%s\", strings.ToUpper(s.String()))\n\tdefault:\n\t\treturn s.String()\n\t}\n}\n"
  },
  {
    "path": "pkg/printers/yaml.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage printers\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"sigs.k8s.io/yaml\"\n\n\t\"github.com/undistro/marvin/pkg/types\"\n)\n\n// YAMLPrinter implements a Printer that prints the report in YAML format\ntype YAMLPrinter struct{}\n\nfunc (*YAMLPrinter) PrintObj(report types.Report, w io.Writer) error {\n\tdata, err := yaml.Marshal(report)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = fmt.Fprint(w, string(data))\n\treturn err\n}\n"
  },
  {
    "path": "pkg/types/check.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage types\n\nimport (\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\t\"k8s.io/apimachinery/pkg/version\"\n)\n\ntype Check struct {\n\tID          string            `json:\"id\"`\n\tMatch       Match             `json:\"match\"`\n\tValidations []Validation      `json:\"validations\"`\n\tVariables   []Variable        `json:\"variables\"`\n\tParams      map[string]any    `json:\"params\"`\n\tSeverity    Severity          `json:\"severity\"`\n\tMessage     string            `json:\"message\"`\n\tLabels      map[string]string `json:\"labels,omitempty\"`\n\n\tBuiltin bool   `json:\"builtin\"`\n\tPath    string `json:\"path,omitempty\"`\n}\n\ntype Match struct {\n\tResources []ResourceRule `json:\"resources\"`\n}\n\ntype ResourceRule struct {\n\tGroup    string `json:\"group,omitempty\"`\n\tVersion  string `json:\"version\"`\n\tResource string `json:\"resource\"`\n}\n\nfunc (r *ResourceRule) ToGVR() schema.GroupVersionResource {\n\treturn schema.GroupVersionResource{Group: r.Group, Version: r.Version, Resource: r.Resource}\n}\n\ntype Validation struct {\n\tExpression string `json:\"expression\"`\n\tMessage    string `json:\"message,omitempty\"`\n}\n\ntype Variable struct {\n\tName       string `json:\"name\"`\n\tExpression string `json:\"expression\"`\n}\n\ntype Test struct {\n\tName        string        `json:\"name\"`\n\tInput       string        `json:\"input\"`\n\tParams      any           `json:\"params\"`\n\tAPIVersions []string      `json:\"apiVersions\"`\n\tKubeVersion *version.Info `json:\"kubeVersion\"`\n\tPass        bool          `json:\"pass\"`\n\tMessage     string        `json:\"message\"`\n}\n"
  },
  {
    "path": "pkg/types/check_test.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage types\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n)\n\nfunc TestResourceRule_ToGVR(t *testing.T) {\n\ttype fields struct {\n\t\tGroup    string\n\t\tVersion  string\n\t\tResource string\n\t}\n\ttests := []struct {\n\t\tname   string\n\t\tfields fields\n\t\twant   schema.GroupVersionResource\n\t}{\n\t\t{\n\t\t\tname: \"services\",\n\t\t\tfields: fields{\n\t\t\t\tGroup:    \"\",\n\t\t\t\tVersion:  \"v1\",\n\t\t\t\tResource: \"services\",\n\t\t\t},\n\t\t\twant: corev1.SchemeGroupVersion.WithResource(\"services\"),\n\t\t},\n\t\t{\n\t\t\tname: \"deployments\",\n\t\t\tfields: fields{\n\t\t\t\tGroup:    \"apps\",\n\t\t\t\tVersion:  \"v1\",\n\t\t\t\tResource: \"deployments\",\n\t\t\t},\n\t\t\twant: appsv1.SchemeGroupVersion.WithResource(\"deployments\"),\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tr := &ResourceRule{\n\t\t\t\tGroup:    tt.fields.Group,\n\t\t\t\tVersion:  tt.fields.Version,\n\t\t\t\tResource: tt.fields.Resource,\n\t\t\t}\n\t\t\tassert.Equalf(t, tt.want, r.ToGVR(), \"ToGVR()\")\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/types/report.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage types\n\nimport (\n\t\"fmt\"\n\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/version\"\n)\n\ntype Report struct {\n\tKubeVersion *version.Info     `json:\"kubeVersion\"`\n\tChecks      []*CheckResult    `json:\"checks\"`\n\tGVRs        map[string]string `json:\"gvrs,omitempty\"`\n}\n\nfunc NewReport(kubeVersion *version.Info) *Report {\n\treturn &Report{KubeVersion: kubeVersion}\n}\n\nfunc (r *Report) Add(cr *CheckResult) {\n\tr.Checks = append(r.Checks, cr)\n}\n\nfunc (r *Report) HasError() bool {\n\tfor _, check := range r.Checks {\n\t\tif len(check.Errors) > 0 {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\ntype CheckResult struct {\n\tID       string            `json:\"id\"`\n\tMessage  string            `json:\"message\"`\n\tSeverity Severity          `json:\"severity\"`\n\tBuiltin  bool              `json:\"builtin\"`\n\tPath     string            `json:\"path\"`\n\tLabels   map[string]string `json:\"labels,omitempty\"`\n\n\tStatus  CheckStatus         `json:\"status\"`\n\tFailed  map[string][]string `json:\"failed\"`\n\tPassed  map[string][]string `json:\"passed\"`\n\tSkipped map[string][]string `json:\"skipped\"`\n\tErrors  []string            `json:\"errors\"`\n\n\tTotalFailed  int `json:\"totalFailed\"`\n\tTotalPassed  int `json:\"totalPassed\"`\n\tTotalSkipped int `json:\"totalSkipped\"`\n}\n\nfunc NewCheckResult(check Check) *CheckResult {\n\treturn &CheckResult{\n\t\tID:       check.ID,\n\t\tMessage:  check.Message,\n\t\tSeverity: check.Severity,\n\t\tBuiltin:  check.Builtin,\n\t\tPath:     check.Path,\n\t\tLabels:   check.Labels,\n\n\t\tFailed:  map[string][]string{},\n\t\tPassed:  map[string][]string{},\n\t\tSkipped: map[string][]string{},\n\t\tErrors:  []string{},\n\t}\n}\n\nfunc (r *CheckResult) AddFailed(obj unstructured.Unstructured) {\n\taddResource(obj, r.Failed)\n\tr.TotalFailed++\n}\n\nfunc (r *CheckResult) AddPassed(obj unstructured.Unstructured) {\n\taddResource(obj, r.Passed)\n\tr.TotalPassed++\n}\n\nfunc (r *CheckResult) AddSkipped(obj unstructured.Unstructured) {\n\taddResource(obj, r.Skipped)\n\tr.TotalSkipped++\n}\n\nfunc (r *CheckResult) AddError(err error) {\n\tr.Errors = append(r.Errors, err.Error())\n}\n\nfunc (r *CheckResult) AddErrors(errs ...error) {\n\tfor _, err := range errs {\n\t\tr.AddError(err)\n\t}\n}\n\nfunc (r *CheckResult) UpdateStatus() {\n\tif len(r.Errors) > 0 {\n\t\tr.Status = StatusError\n\t\treturn\n\t}\n\tif len(r.Failed) > 0 {\n\t\tr.Status = StatusFailed\n\t\treturn\n\t}\n\tif len(r.Passed) == 0 && len(r.Skipped) > 0 {\n\t\tr.Status = StatusSkipped\n\t\treturn\n\t}\n\tr.Status = StatusPassed\n\treturn\n}\n\n// GVK returns the GroupVersionKind string of the given resource\nfunc GVK(obj unstructured.Unstructured) string {\n\tgvk := obj.GroupVersionKind()\n\treturn fmt.Sprintf(\"%s/%s\", gvk.GroupVersion().String(), gvk.Kind)\n}\n\n// NamespacedName returns the namespaced name string of the given resource\nfunc NamespacedName(obj unstructured.Unstructured) string {\n\tif len(obj.GetNamespace()) > 0 {\n\t\treturn fmt.Sprintf(\"%s/%s\", obj.GetNamespace(), obj.GetName())\n\t}\n\treturn obj.GetName()\n}\n\nfunc addResource(obj unstructured.Unstructured, m map[string][]string) {\n\tk := GVK(obj)\n\tv := NamespacedName(obj)\n\tif _, ok := m[k]; ok {\n\t\tm[k] = append(m[k], v)\n\t} else {\n\t\tm[k] = []string{v}\n\t}\n}\n"
  },
  {
    "path": "pkg/types/report_test.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage types\n\nimport (\n\t\"errors\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/version\"\n)\n\nvar (\n\tkubeVersion = &version.Info{\n\t\tMajor:      \"1\",\n\t\tMinor:      \"25\",\n\t\tGitVersion: \"v1.25.3\",\n\t}\n\tcheck = Check{\n\t\tID:       \"foo\",\n\t\tSeverity: SeverityHigh,\n\t\tMessage:  \"bar\",\n\t\tBuiltin:  true,\n\t\tPath:     \"path.yml\",\n\t}\n)\n\nfunc TestReport(t *testing.T) {\n\trep := NewReport(kubeVersion)\n\tassert.NotNil(t, rep)\n\tassert.NotNil(t, rep.KubeVersion)\n\tassert.Equal(t, \"25\", rep.KubeVersion.Minor)\n\tassert.Len(t, rep.Checks, 0)\n\n\tcr := NewCheckResult(check)\n\tassert.NotNil(t, cr)\n\tassert.Equal(t, \"foo\", cr.ID)\n\tassert.Equal(t, \"bar\", cr.Message)\n\tassert.Equal(t, SeverityHigh, cr.Severity)\n\tassert.True(t, cr.Builtin)\n\tassert.Equal(t, \"path.yml\", cr.Path)\n\tassert.Len(t, cr.Failed, 0)\n\tassert.Len(t, cr.Passed, 0)\n\tassert.Len(t, cr.Skipped, 0)\n\tassert.Len(t, cr.Errors, 0)\n\n\tcr.AddSkipped(obj(\"apps/v1\", \"Deployment\", \"ns\", \"skipped-deploy-1\"))\n\tcr.AddSkipped(obj(\"apps/v1\", \"Deployment\", \"ns\", \"skipped-deploy-2\"))\n\tcr.AddSkipped(obj(\"v1\", \"Pod\", \"\", \"skipped-pod\"))\n\n\tassert.Len(t, cr.Skipped, 2)\n\tassert.Len(t, cr.Skipped[\"apps/v1/Deployment\"], 2)\n\tassert.Len(t, cr.Skipped[\"v1/Pod\"], 1)\n\tassert.Len(t, cr.Failed, 0)\n\tassert.Len(t, cr.Passed, 0)\n\tassert.Len(t, cr.Errors, 0)\n\n\tcr.UpdateStatus()\n\n\tassert.Equal(t, StatusSkipped, cr.Status)\n\n\tcr.AddPassed(obj(\"v1\", \"Pod\", \"ns\", \"passed-1\"))\n\tcr.AddPassed(obj(\"v1\", \"Pod\", \"\", \"passed-2\"))\n\n\tassert.Len(t, cr.Skipped, 2)\n\tassert.Len(t, cr.Passed, 1)\n\tassert.Len(t, cr.Passed[\"v1/Pod\"], 2)\n\tassert.Equal(t, cr.Passed[\"v1/Pod\"][0], \"ns/passed-1\")\n\tassert.Equal(t, cr.Passed[\"v1/Pod\"][1], \"passed-2\")\n\tassert.Len(t, cr.Failed, 0)\n\tassert.Len(t, cr.Errors, 0)\n\n\tcr.UpdateStatus()\n\n\tassert.Equal(t, StatusPassed, cr.Status)\n\n\tcr.AddFailed(obj(\"batch/v1\", \"CronJob\", \"ns\", \"failed\"))\n\n\tassert.Len(t, cr.Skipped, 2)\n\tassert.Len(t, cr.Passed, 1)\n\tassert.Len(t, cr.Failed, 1)\n\tassert.Len(t, cr.Failed[\"batch/v1/CronJob\"], 1)\n\tassert.Len(t, cr.Errors, 0)\n\n\tcr.UpdateStatus()\n\n\tassert.Equal(t, StatusFailed, cr.Status)\n\n\tcr.AddError(errors.New(\"list deployments error\"))\n\tassert.Len(t, cr.Skipped, 2)\n\tassert.Len(t, cr.Passed, 1)\n\tassert.Len(t, cr.Failed, 1)\n\tassert.Len(t, cr.Errors, 1)\n\n\tcr.UpdateStatus()\n\n\tassert.Equal(t, StatusError, cr.Status)\n\n\trep.Add(cr)\n\n\tassert.Len(t, rep.Checks, 1)\n}\n\nfunc obj(apiVersion, kind, ns, name string) unstructured.Unstructured {\n\treturn unstructured.Unstructured{Object: map[string]any{\n\t\t\"apiVersion\": apiVersion,\n\t\t\"kind\":       kind,\n\t\t\"metadata\":   map[string]any{\"namespace\": ns, \"name\": name},\n\t}}\n}\n"
  },
  {
    "path": "pkg/types/severity.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage types\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype Severity int\n\nconst (\n\tSeverityUnknown Severity = iota\n\tSeverityLow\n\tSeverityMedium\n\tSeverityHigh\n\tSeverityCritical\n)\n\nfunc (s Severity) String() string {\n\tswitch s {\n\tcase SeverityLow:\n\t\treturn \"Low\"\n\tcase SeverityMedium:\n\t\treturn \"Medium\"\n\tcase SeverityHigh:\n\t\treturn \"High\"\n\tcase SeverityCritical:\n\t\treturn \"Critical\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\nfunc ParseSeverity(s string) Severity {\n\tswitch s {\n\tcase \"Low\":\n\t\treturn SeverityLow\n\tcase \"Medium\":\n\t\treturn SeverityMedium\n\tcase \"High\":\n\t\treturn SeverityHigh\n\tcase \"Critical\":\n\t\treturn SeverityCritical\n\tdefault:\n\t\treturn SeverityUnknown\n\t}\n}\n\nfunc (s *Severity) UnmarshalJSON(b []byte) error {\n\t*s = ParseSeverity(strings.Trim(string(b), \"\\\"\"))\n\treturn nil\n}\n\nfunc (s Severity) MarshalJSON() ([]byte, error) {\n\tv := fmt.Sprintf(`\"%s\"`, s.String())\n\treturn []byte(v), nil\n}\n"
  },
  {
    "path": "pkg/types/severity_test.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage types\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype severityHolder struct {\n\tSeverity Severity `json:\"severity\"`\n}\n\nfunc TestSeverityMarshalJSON(t *testing.T) {\n\ttests := []struct {\n\t\tinput Severity\n\t\twant  string\n\t}{\n\t\t{\n\t\t\tinput: SeverityLow,\n\t\t\twant:  `{\"severity\":\"Low\"}`,\n\t\t},\n\t\t{\n\t\t\tinput: SeverityMedium,\n\t\t\twant:  `{\"severity\":\"Medium\"}`,\n\t\t},\n\t\t{\n\t\t\tinput: SeverityHigh,\n\t\t\twant:  `{\"severity\":\"High\"}`,\n\t\t},\n\t\t{\n\t\t\tinput: SeverityCritical,\n\t\t\twant:  `{\"severity\":\"Critical\"}`,\n\t\t},\n\t\t{\n\t\t\tinput: SeverityCritical,\n\t\t\twant:  `{\"severity\":\"Critical\"}`,\n\t\t},\n\t\t{\n\t\t\tinput: SeverityUnknown,\n\t\t\twant:  `{\"severity\":\"\"}`,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.input.String(), func(t *testing.T) {\n\t\t\tinput := severityHolder{tt.input}\n\t\t\tgot, err := json.Marshal(&input)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, tt.want, string(got))\n\t\t})\n\t}\n}\n\nfunc TestSeverityUnmarshalJSON(t *testing.T) {\n\ttests := []struct {\n\t\tinput string\n\t\twant  Severity\n\t}{\n\t\t{\n\t\t\tinput: `{\"severity\": \"Low\"}`,\n\t\t\twant:  SeverityLow,\n\t\t},\n\t\t{\n\t\t\tinput: `{\"severity\": \"Medium\"}`,\n\t\t\twant:  SeverityMedium,\n\t\t},\n\t\t{\n\t\t\tinput: `{\"severity\": \"High\"}`,\n\t\t\twant:  SeverityHigh,\n\t\t},\n\t\t{\n\t\t\tinput: `{\"severity\": \"Critical\"}`,\n\t\t\twant:  SeverityCritical,\n\t\t},\n\t\t{\n\t\t\tinput: `{\"severity\": \"Critical\"}`,\n\t\t\twant:  SeverityCritical,\n\t\t},\n\t\t{\n\t\t\tinput: `{\"severity\": \"foo\"}`,\n\t\t\twant:  SeverityUnknown,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.want.String(), func(t *testing.T) {\n\t\t\tvar got severityHolder\n\t\t\terr := json.Unmarshal([]byte(tt.input), &got)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, tt.want, got.Severity)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/types/status.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage types\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\ntype CheckStatus int\n\nconst (\n\tStatusUnknown CheckStatus = iota\n\tStatusPassed\n\tStatusSkipped\n\tStatusFailed\n\tStatusError\n)\n\nfunc (s CheckStatus) String() string {\n\tswitch s {\n\tcase StatusPassed:\n\t\treturn \"Passed\"\n\tcase StatusSkipped:\n\t\treturn \"Skipped\"\n\tcase StatusFailed:\n\t\treturn \"Failed\"\n\tcase StatusError:\n\t\treturn \"Error\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\nfunc ParseStatus(s string) CheckStatus {\n\tswitch s {\n\tcase \"Passed\":\n\t\treturn StatusPassed\n\tcase \"Skipped\":\n\t\treturn StatusSkipped\n\tcase \"Failed\":\n\t\treturn StatusFailed\n\tcase \"Error\":\n\t\treturn StatusError\n\tdefault:\n\t\treturn StatusUnknown\n\t}\n}\n\nfunc (s *CheckStatus) UnmarshalJSON(b []byte) error {\n\t*s = ParseStatus(strings.Trim(string(b), \"\\\"\"))\n\treturn nil\n}\n\nfunc (s CheckStatus) MarshalJSON() ([]byte, error) {\n\tv := fmt.Sprintf(`\"%s\"`, s.String())\n\treturn []byte(v), nil\n}\n"
  },
  {
    "path": "pkg/types/status_test.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage types\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype statusHolder struct {\n\tStatus CheckStatus `json:\"status\"`\n}\n\nfunc TestCheckStatusMarshalJSON(t *testing.T) {\n\ttests := []struct {\n\t\tinput CheckStatus\n\t\twant  string\n\t}{\n\t\t{\n\t\t\tinput: StatusError,\n\t\t\twant:  `{\"status\":\"Error\"}`,\n\t\t},\n\t\t{\n\t\t\tinput: StatusFailed,\n\t\t\twant:  `{\"status\":\"Failed\"}`,\n\t\t},\n\t\t{\n\t\t\tinput: StatusPassed,\n\t\t\twant:  `{\"status\":\"Passed\"}`,\n\t\t},\n\t\t{\n\t\t\tinput: StatusSkipped,\n\t\t\twant:  `{\"status\":\"Skipped\"}`,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.input.String(), func(t *testing.T) {\n\t\t\tgot, err := json.Marshal(&statusHolder{tt.input})\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, tt.want, string(got))\n\t\t})\n\t}\n}\n\nfunc TestCheckStatusUnmarshalJSON(t *testing.T) {\n\ttests := []struct {\n\t\tinput string\n\t\twant  CheckStatus\n\t}{\n\t\t{\n\t\t\tinput: `{\"status\": \"Passed\"}`,\n\t\t\twant:  StatusPassed,\n\t\t},\n\t\t{\n\t\t\tinput: `{\"status\": \"Skipped\"}`,\n\t\t\twant:  StatusSkipped,\n\t\t},\n\t\t{\n\t\t\tinput: `{\"status\": \"Failed\"}`,\n\t\t\twant:  StatusFailed,\n\t\t},\n\t\t{\n\t\t\tinput: `{\"status\": \"Error\"}`,\n\t\t\twant:  StatusError,\n\t\t},\n\t\t{\n\t\t\tinput: `{\"status\": \"foo\"}`,\n\t\t\twant:  StatusUnknown,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.want.String(), func(t *testing.T) {\n\t\t\tvar got statusHolder\n\t\t\terr := json.Unmarshal([]byte(tt.input), &got)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.Equal(t, tt.want, got.Status)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/validator/activation.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage validator\n\nimport (\n\t\"github.com/google/cel-go/interpreter\"\n)\n\nconst (\n\tObjectVarName        = \"object\"\n\tParamsVarName        = \"params\"\n\tPodMetaVarName       = \"podMeta\"\n\tPodSpecVarName       = \"podSpec\"\n\tAllContainersVarName = \"allContainers\"\n\tAPIVersionsVarName   = \"apiVersions\"\n\tKubeVersionVarName   = \"kubeVersion\"\n\tVariableVarName      = \"variables\"\n)\n\n// activation implements the interpreter.Activation\ntype activation struct {\n\tobject        map[string]any\n\tpodMeta       map[string]any\n\tpodSpec       map[string]any\n\tallContainers []map[string]any\n\tparams        any\n\tapiVersions   []string\n\tkubeVersion   any\n\tvariables     any\n}\n\nfunc (a *activation) ResolveName(name string) (any, bool) {\n\tswitch name {\n\tcase ObjectVarName:\n\t\treturn a.object, true\n\tcase PodMetaVarName:\n\t\treturn a.podMeta, true\n\tcase PodSpecVarName:\n\t\treturn a.podSpec, true\n\tcase AllContainersVarName:\n\t\treturn a.allContainers, true\n\tcase ParamsVarName:\n\t\treturn a.params, true\n\tcase APIVersionsVarName:\n\t\treturn a.apiVersions, true\n\tcase KubeVersionVarName:\n\t\treturn a.kubeVersion, true\n\tcase VariableVarName:\n\t\treturn a.variables, true\n\tdefault:\n\t\treturn nil, false\n\t}\n}\n\nfunc (a *activation) Parent() interpreter.Activation {\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/validator/compiler.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage validator\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/google/cel-go/cel\"\n\t\"github.com/google/cel-go/ext\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/version\"\n\tk8scellib \"k8s.io/apiserver/pkg/cel/library\"\n\n\t\"github.com/undistro/marvin/pkg/types\"\n)\n\nvar baseEnvOptions = []cel.EnvOption{\n\tcel.HomogeneousAggregateLiterals(),\n\tcel.EagerlyValidateDeclarations(true),\n\tcel.DefaultUTCTimeZone(true),\n\tcel.CrossTypeNumericComparisons(true),\n\tcel.OptionalTypes(),\n\tcel.ASTValidators(\n\t\tcel.ValidateDurationLiterals(),\n\t\tcel.ValidateTimestampLiterals(),\n\t\tcel.ValidateRegexLiterals(),\n\t\tcel.ValidateHomogeneousAggregateLiterals(),\n\t),\n\n\text.Strings(ext.StringsVersion(2)),\n\text.Sets(),\n\text.TwoVarComprehensions(),\n\text.Lists(ext.ListsVersion(3)),\n\n\tk8scellib.URLs(),\n\tk8scellib.Regex(),\n\tk8scellib.Lists(),\n\tk8scellib.Quantity(),\n\tk8scellib.IP(),\n\tk8scellib.CIDR(),\n\tk8scellib.Format(),\n\tk8scellib.SemverLib(k8scellib.SemverVersion(1)),\n\n\tcel.Variable(ObjectVarName, cel.DynType),\n\tcel.Variable(APIVersionsVarName, cel.ListType(cel.StringType)),\n\tcel.Variable(KubeVersionVarName, cel.DynType),\n}\n\nvar programOptions = []cel.ProgramOption{\n\tcel.EvalOptions(cel.OptOptimize),\n}\n\nvar podSpecEnvOptions = []cel.EnvOption{\n\tcel.Variable(PodMetaVarName, cel.DynType),\n\tcel.Variable(PodSpecVarName, cel.DynType),\n\tcel.Variable(AllContainersVarName, cel.ListType(cel.DynType)),\n}\n\n// Compile compiles variables and expressions of the given check and returns a Validator\nfunc Compile(check types.Check, apiResources []*metav1.APIResourceList, kubeVersion *version.Info, costLimit uint64) (Validator, error) {\n\tif len(check.Validations) == 0 {\n\t\treturn nil, errors.New(\"invalid check: a check must have at least 1 validation\")\n\t}\n\tenv, err := newEnv(check)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"environment construction error %s\", err.Error())\n\t}\n\n\tvariables, err := compileVariables(env, check.Variables, costLimit)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tprgs, err := compileValidations(env, check.Validations, costLimit)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tapiVersions := make([]string, 0, len(apiResources))\n\tfor _, resource := range apiResources {\n\t\tapiVersions = append(apiVersions, resource.GroupVersion)\n\t}\n\treturn &CELValidator{check: check, programs: prgs, apiVersions: apiVersions, kubeVersion: kubeVersion, variables: variables}, nil\n}\n\nfunc newEnv(check types.Check) (*cel.Env, error) {\n\topts := baseEnvOptions\n\tif MatchesPodSpec(check.Match.Resources) {\n\t\topts = append(opts, podSpecEnvOptions...)\n\t}\n\tif len(check.Variables) > 0 {\n\t\topts = append(opts, cel.Variable(VariableVarName, cel.MapType(cel.StringType, cel.DynType)))\n\t}\n\tif len(check.Params) > 0 {\n\t\topts = append(opts, cel.Variable(ParamsVarName, cel.DynType))\n\t}\n\treturn cel.NewEnv(opts...)\n}\n\nfunc compileVariables(env *cel.Env, vars []types.Variable, costLimit uint64) ([]compiledVariable, error) {\n\tvariables := make([]compiledVariable, 0, len(vars))\n\tfor _, v := range vars {\n\t\tprg, err := compileExpression(env, v.Expression, costLimit, cel.AnyType)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"variables[%q].expression: %s\", v.Name, err)\n\t\t}\n\t\tvariables = append(variables, compiledVariable{name: v.Name, program: prg})\n\t}\n\treturn variables, nil\n}\n\nfunc compileValidations(env *cel.Env, vals []types.Validation, costLimit uint64) ([]cel.Program, error) {\n\tprgs := make([]cel.Program, 0, len(vals))\n\tfor i, v := range vals {\n\t\tprg, err := compileExpression(env, v.Expression, costLimit, cel.BoolType)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"validations[%d].expression: %s\", i, err)\n\t\t}\n\t\tprgs = append(prgs, prg)\n\t}\n\treturn prgs, nil\n}\n\nfunc compileExpression(env *cel.Env, exp string, costLimit uint64, allowedTypes ...*cel.Type) (cel.Program, error) {\n\tast, issues := env.Compile(exp)\n\tif issues != nil && issues.Err() != nil {\n\t\treturn nil, fmt.Errorf(\"type-check error: %s\", issues.Err())\n\t}\n\tfound := false\n\tfor _, t := range allowedTypes {\n\t\tif ast.OutputType() == t || cel.AnyType == t {\n\t\t\tfound = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !found {\n\t\tif len(allowedTypes) == 1 {\n\t\t\treturn nil, fmt.Errorf(\"must evaluate to %v\", allowedTypes[0].String())\n\t\t}\n\t\treturn nil, fmt.Errorf(\"must evaluate to one of %v\", allowedTypes)\n\t}\n\topts := programOptions\n\tif costLimit > 0 {\n\t\topts = append(opts, cel.CostLimit(costLimit))\n\t}\n\tprg, err := env.Program(ast, opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"program construction error: %s\", err)\n\t}\n\treturn prg, nil\n}\n"
  },
  {
    "path": "pkg/validator/compiler_test.go",
    "content": "// Copyright 2024 Undistro Authors\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.\n\npackage validator\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/undistro/marvin/pkg/types\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/version\"\n)\n\nfunc TestCompile(t *testing.T) {\n\tvar apiResources []*metav1.APIResourceList\n\tkubeVersion := &version.Info{Major: \"1\", Minor: \"29\", GitVersion: \"v1.29.2\"}\n\tpodsMatch := types.Match{Resources: []types.ResourceRule{{\n\t\tGroup:    \"\",\n\t\tVersion:  \"v1\",\n\t\tResource: \"pods\",\n\t}}}\n\n\ttests := []struct {\n\t\tcheck   types.Check\n\t\twantErr assert.ErrorAssertionFunc\n\t}{\n\t\t{\n\t\t\tcheck: types.Check{\n\t\t\t\tID:    \"ok\",\n\t\t\t\tMatch: podsMatch,\n\t\t\t\tValidations: []types.Validation{{\n\t\t\t\t\tExpression: `variables.isWindows || allContainers.size() > 0`,\n\t\t\t\t}},\n\t\t\t\tVariables: []types.Variable{{\n\t\t\t\t\tName:       \"isWindows\",\n\t\t\t\t\tExpression: `podSpec.?os.?name.orValue(\"\") == \"windows\"`,\n\t\t\t\t}},\n\t\t\t},\n\t\t\twantErr: assert.NoError,\n\t\t},\n\t\t{\n\t\t\tcheck: types.Check{\n\t\t\t\tID:    \"validation error\",\n\t\t\t\tMatch: podsMatch,\n\t\t\t\tValidations: []types.Validation{{\n\t\t\t\t\tExpression: `allContainers.sizeX() > 0`,\n\t\t\t\t}},\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tcheck: types.Check{\n\t\t\t\tID:    \"variable error\",\n\t\t\t\tMatch: podsMatch,\n\t\t\t\tValidations: []types.Validation{{\n\t\t\t\t\tExpression: `variables.isWindows || allContainers.size() > 0`,\n\t\t\t\t}},\n\t\t\t\tVariables: []types.Variable{{\n\t\t\t\t\tName:       \"isWindows\",\n\t\t\t\t\tExpression: `foo`,\n\t\t\t\t}},\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tcheck: types.Check{\n\t\t\t\tID: \"no workload\",\n\t\t\t\tMatch: types.Match{Resources: []types.ResourceRule{{\n\t\t\t\t\tGroup:    \"\",\n\t\t\t\t\tVersion:  \"v1\",\n\t\t\t\t\tResource: \"configmaps\",\n\t\t\t\t}}},\n\t\t\t\tValidations: []types.Validation{{\n\t\t\t\t\tExpression: `allContainers.size() > 0`,\n\t\t\t\t}},\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t\t{\n\t\t\tcheck: types.Check{\n\t\t\t\tID:          \"no validations\",\n\t\t\t\tMatch:       podsMatch,\n\t\t\t\tValidations: nil,\n\t\t\t\tVariables: []types.Variable{{\n\t\t\t\t\tName:       \"isWindows\",\n\t\t\t\t\tExpression: `podSpec.?os.?name.orValue(\"\") == \"windows\"`,\n\t\t\t\t}},\n\t\t\t},\n\t\t\twantErr: assert.Error,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.check.ID, func(t *testing.T) {\n\t\t\t_, err := Compile(tt.check, apiResources, kubeVersion, 1000000)\n\t\t\tif !tt.wantErr(t, err, fmt.Sprintf(\"Compile(%v, %v, %v)\", tt.check, apiResources, kubeVersion)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/validator/interface.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage validator\n\nimport (\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/version\"\n)\n\n// Validator is an interface that defines a method for validating a k8s unustructured object\ntype Validator interface {\n\tValidate(obj unstructured.Unstructured, params any) (bool, string, error)\n\tSetAPIVersions(apiVersions []string)\n\tSetKubeVersion(v *version.Info)\n}\n"
  },
  {
    "path": "pkg/validator/podspec.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage validator\n\nimport (\n\t\"fmt\"\n\n\tappsv1 \"k8s.io/api/apps/v1\"\n\tbatchv1 \"k8s.io/api/batch/v1\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tmetav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/runtime/schema\"\n\n\t\"github.com/undistro/marvin/pkg/types\"\n)\n\n// MatchesPodSpec returns true if any rule matches a Pod spec\nfunc MatchesPodSpec(rules []types.ResourceRule) bool {\n\tfor _, r := range rules {\n\t\tgr := r.ToGVR().GroupResource()\n\t\tif defaultPodSpecResources[gr] {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nvar defaultPodSpecResources = map[schema.GroupResource]bool{\n\tcorev1.Resource(\"pods\"):                   true,\n\tcorev1.Resource(\"replicationcontrollers\"): true,\n\tcorev1.Resource(\"podtemplates\"):           true,\n\tappsv1.Resource(\"replicasets\"):            true,\n\tappsv1.Resource(\"deployments\"):            true,\n\tappsv1.Resource(\"statefulsets\"):           true,\n\tappsv1.Resource(\"daemonsets\"):             true,\n\tbatchv1.Resource(\"jobs\"):                  true,\n\tbatchv1.Resource(\"cronjobs\"):              true,\n}\n\n// HasPodSpec returns true if the given object has a Pod spec\nfunc HasPodSpec(u unstructured.Unstructured) bool {\n\tgk := u.GroupVersionKind().GroupKind()\n\t_, ok := defaultPodSpecTypes[gk]\n\treturn ok\n}\n\n// ExtractPodSpec returns the metadata and Pod spec from the given object\nfunc ExtractPodSpec(u unstructured.Unstructured) (*metav1.ObjectMeta, *corev1.PodSpec, error) {\n\tgk := u.GroupVersionKind().GroupKind()\n\tobj, ok := defaultPodSpecTypes[gk]\n\tif !ok {\n\t\treturn nil, nil, fmt.Errorf(\"unexpected object type: %s\", u.GetObjectKind().GroupVersionKind().String())\n\t}\n\terr := runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), obj)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"from unstructured converter error: %s\", err.Error())\n\t}\n\tswitch o := obj.(type) {\n\tcase *corev1.Pod:\n\t\treturn &o.ObjectMeta, &o.Spec, nil\n\tcase *corev1.PodTemplate:\n\t\treturn extractPodSpecFromTemplate(&o.Template)\n\tcase *corev1.ReplicationController:\n\t\treturn extractPodSpecFromTemplate(o.Spec.Template)\n\tcase *appsv1.ReplicaSet:\n\t\treturn extractPodSpecFromTemplate(&o.Spec.Template)\n\tcase *appsv1.Deployment:\n\t\treturn extractPodSpecFromTemplate(&o.Spec.Template)\n\tcase *appsv1.DaemonSet:\n\t\treturn extractPodSpecFromTemplate(&o.Spec.Template)\n\tcase *appsv1.StatefulSet:\n\t\treturn extractPodSpecFromTemplate(&o.Spec.Template)\n\tcase *batchv1.Job:\n\t\treturn extractPodSpecFromTemplate(&o.Spec.Template)\n\tcase *batchv1.CronJob:\n\t\treturn extractPodSpecFromTemplate(&o.Spec.JobTemplate.Spec.Template)\n\tdefault:\n\t\treturn nil, nil, fmt.Errorf(\"unexpected object type: %s\", u.GetObjectKind().GroupVersionKind().String())\n\t}\n}\n\nvar defaultPodSpecTypes = map[schema.GroupKind]any{\n\tcorev1.SchemeGroupVersion.WithKind(\"Pod\").GroupKind():                   &corev1.Pod{},\n\tcorev1.SchemeGroupVersion.WithKind(\"ReplicationController\").GroupKind(): &corev1.ReplicationController{},\n\tcorev1.SchemeGroupVersion.WithKind(\"PodTemplate\").GroupKind():           &corev1.PodTemplate{},\n\tappsv1.SchemeGroupVersion.WithKind(\"ReplicaSet\").GroupKind():            &appsv1.ReplicaSet{},\n\tappsv1.SchemeGroupVersion.WithKind(\"Deployment\").GroupKind():            &appsv1.Deployment{},\n\tappsv1.SchemeGroupVersion.WithKind(\"StatefulSet\").GroupKind():           &appsv1.StatefulSet{},\n\tappsv1.SchemeGroupVersion.WithKind(\"DaemonSet\").GroupKind():             &appsv1.DaemonSet{},\n\tbatchv1.SchemeGroupVersion.WithKind(\"Job\").GroupKind():                  &batchv1.Job{},\n\tbatchv1.SchemeGroupVersion.WithKind(\"CronJob\").GroupKind():              &batchv1.CronJob{},\n}\n\nfunc extractPodSpecFromTemplate(template *corev1.PodTemplateSpec) (*metav1.ObjectMeta, *corev1.PodSpec, error) {\n\tif template == nil {\n\t\treturn nil, nil, nil\n\t}\n\treturn &template.ObjectMeta, &template.Spec, nil\n}\n\nfunc extractAllContainers(podSpec *corev1.PodSpec) []corev1.Container {\n\tcontainers := append(podSpec.Containers, podSpec.InitContainers...)\n\tfor _, ec := range podSpec.EphemeralContainers {\n\t\tc := corev1.Container{\n\t\t\tName:                     ec.Name,\n\t\t\tImage:                    ec.Image,\n\t\t\tCommand:                  ec.Command,\n\t\t\tArgs:                     ec.Args,\n\t\t\tWorkingDir:               ec.WorkingDir,\n\t\t\tPorts:                    ec.Ports,\n\t\t\tEnvFrom:                  ec.EnvFrom,\n\t\t\tEnv:                      ec.Env,\n\t\t\tResources:                ec.Resources,\n\t\t\tVolumeMounts:             ec.VolumeMounts,\n\t\t\tVolumeDevices:            ec.VolumeDevices,\n\t\t\tLivenessProbe:            ec.LivenessProbe,\n\t\t\tReadinessProbe:           ec.ReadinessProbe,\n\t\t\tStartupProbe:             ec.StartupProbe,\n\t\t\tLifecycle:                ec.Lifecycle,\n\t\t\tTerminationMessagePath:   ec.TerminationMessagePath,\n\t\t\tTerminationMessagePolicy: ec.TerminationMessagePolicy,\n\t\t\tImagePullPolicy:          ec.ImagePullPolicy,\n\t\t\tSecurityContext:          ec.SecurityContext,\n\t\t\tStdin:                    ec.Stdin,\n\t\t\tStdinOnce:                ec.StdinOnce,\n\t\t\tTTY:                      ec.TTY,\n\t\t}\n\t\tcontainers = append(containers, c)\n\t}\n\treturn containers\n}\n"
  },
  {
    "path": "pkg/validator/podspec_test.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage validator\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\tcorev1 \"k8s.io/api/core/v1\"\n\tv1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\n\t\"github.com/undistro/marvin/pkg/types\"\n)\n\nfunc TestMatchesPodSpec(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\trules []types.ResourceRule\n\t\twant  bool\n\t}{\n\t\t{\n\t\t\tname: \"deployments\",\n\t\t\trules: []types.ResourceRule{{\n\t\t\t\tGroup:    \"apps\",\n\t\t\t\tVersion:  \"v1\",\n\t\t\t\tResource: \"deployments\",\n\t\t\t}},\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\tname: \"pods and services\",\n\t\t\trules: []types.ResourceRule{\n\t\t\t\t{\n\t\t\t\t\tGroup:    \"\",\n\t\t\t\t\tVersion:  \"v1\",\n\t\t\t\t\tResource: \"pods\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGroup:    \"\",\n\t\t\t\t\tVersion:  \"v1\",\n\t\t\t\t\tResource: \"services\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\tname: \"services and cronjobs\",\n\t\t\trules: []types.ResourceRule{\n\t\t\t\t{\n\t\t\t\t\tGroup:    \"\",\n\t\t\t\t\tVersion:  \"v1\",\n\t\t\t\t\tResource: \"services\",\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tGroup:    \"batch\",\n\t\t\t\t\tVersion:  \"v1\",\n\t\t\t\t\tResource: \"cronjobs\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twant: true,\n\t\t},\n\t\t{\n\t\t\tname: \"services\",\n\t\t\trules: []types.ResourceRule{{\n\t\t\t\tGroup:    \"\",\n\t\t\t\tVersion:  \"v1\",\n\t\t\t\tResource: \"services\",\n\t\t\t}},\n\t\t\twant: false,\n\t\t},\n\t\t{\n\t\t\tname:  \"empty\",\n\t\t\trules: []types.ResourceRule{},\n\t\t\twant:  false,\n\t\t},\n\t\t{\n\t\t\tname:  \"nil\",\n\t\t\trules: nil,\n\t\t\twant:  false,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := MatchesPodSpec(tt.rules); got != tt.want {\n\t\t\t\tt.Errorf(\"MatchesPodSpec() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestExtractPodSpec(t *testing.T) {\n\tmetadata := map[string]any{\n\t\t\"name\": \"foo-pod\",\n\t}\n\texpectedMeta := &v1.ObjectMeta{\n\t\tName: \"foo-pod\",\n\t}\n\tspec := map[string]any{\n\t\t\"containers\": []map[string]any{{\n\t\t\t\"name\": \"foo-container\",\n\t\t}},\n\t}\n\texpectedSpec := &corev1.PodSpec{\n\t\tContainers: []corev1.Container{{Name: \"foo-container\"}},\n\t}\n\tobjects := []map[string]any{\n\t\t{\n\t\t\t\"apiVersion\": \"v1\",\n\t\t\t\"kind\":       \"Pod\",\n\t\t\t\"metadata\":   metadata,\n\t\t\t\"spec\":       spec,\n\t\t},\n\t\t{\n\t\t\t\"apiVersion\": \"v1\",\n\t\t\t\"kind\":       \"PodTemplate\",\n\t\t\t\"metadata\":   map[string]any{\"name\": \"foo-template\"},\n\t\t\t\"template\": map[string]any{\n\t\t\t\t\"metadata\": metadata,\n\t\t\t\t\"spec\":     spec,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"apiVersion\": \"v1\",\n\t\t\t\"kind\":       \"ReplicationController\",\n\t\t\t\"metadata\":   map[string]any{\"name\": \"foo-rc\"},\n\t\t\t\"spec\": map[string]any{\n\t\t\t\t\"template\": map[string]any{\n\t\t\t\t\t\"metadata\": metadata,\n\t\t\t\t\t\"spec\":     spec,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\"kind\":       \"ReplicaSet\",\n\t\t\t\"metadata\":   map[string]any{\"name\": \"foo-rs\"},\n\t\t\t\"spec\": map[string]any{\n\t\t\t\t\"template\": map[string]any{\n\t\t\t\t\t\"metadata\": metadata,\n\t\t\t\t\t\"spec\":     spec,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\"kind\":       \"Deployment\",\n\t\t\t\"metadata\":   map[string]any{\"name\": \"foo-deployment\"},\n\t\t\t\"spec\": map[string]any{\n\t\t\t\t\"template\": map[string]any{\n\t\t\t\t\t\"metadata\": metadata,\n\t\t\t\t\t\"spec\":     spec,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\"kind\":       \"StatefulSet\",\n\t\t\t\"metadata\":   map[string]any{\"name\": \"foo-ss\"},\n\t\t\t\"spec\": map[string]any{\n\t\t\t\t\"template\": map[string]any{\n\t\t\t\t\t\"metadata\": metadata,\n\t\t\t\t\t\"spec\":     spec,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"apiVersion\": \"apps/v1\",\n\t\t\t\"kind\":       \"DaemonSet\",\n\t\t\t\"metadata\":   map[string]any{\"name\": \"foo-ds\"},\n\t\t\t\"spec\": map[string]any{\n\t\t\t\t\"template\": map[string]any{\n\t\t\t\t\t\"metadata\": metadata,\n\t\t\t\t\t\"spec\":     spec,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"apiVersion\": \"batch/v1\",\n\t\t\t\"kind\":       \"Job\",\n\t\t\t\"metadata\":   map[string]any{\"name\": \"foo-job\"},\n\t\t\t\"spec\": map[string]any{\n\t\t\t\t\"template\": map[string]any{\n\t\t\t\t\t\"metadata\": metadata,\n\t\t\t\t\t\"spec\":     spec,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"apiVersion\": \"batch/v1\",\n\t\t\t\"kind\":       \"CronJob\",\n\t\t\t\"metadata\":   map[string]any{\"name\": \"foo-cronjob\"},\n\t\t\t\"spec\": map[string]any{\n\t\t\t\t\"jobTemplate\": map[string]any{\n\t\t\t\t\t\"spec\": map[string]any{\n\t\t\t\t\t\t\"template\": map[string]any{\n\t\t\t\t\t\t\t\"metadata\": metadata,\n\t\t\t\t\t\t\t\"spec\":     spec,\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\tfor _, obj := range objects {\n\t\tu := unstructured.Unstructured{Object: obj}\n\t\tname := u.GetName()\n\t\tactualMetadata, actualSpec, err := ExtractPodSpec(u)\n\t\tassert.NoError(t, err, name)\n\t\tassert.Equal(t, expectedMeta, actualMetadata, \"%s: Metadata mismatch\", name)\n\t\tassert.Equal(t, expectedSpec, actualSpec, \"%s: PodSpec mismatch\", name)\n\t}\n\n\tvar service = map[string]any{\n\t\t\"apiVersion\": \"v1\",\n\t\t\"kind\":       \"Service\",\n\t\t\"metadata\": map[string]any{\n\t\t\t\"name\": \"foo-svc\",\n\t\t},\n\t}\n\t_, _, err := ExtractPodSpec(unstructured.Unstructured{Object: service})\n\tassert.Error(t, err, \"service should not have an extractable pod spec\")\n}\n\nfunc TestExtractAllContainers(t *testing.T) {\n\ttests := []struct {\n\t\tname    string\n\t\tpodSpec *corev1.PodSpec\n\t\twant    []corev1.Container\n\t}{\n\t\t{\n\t\t\tname:    \"single container\",\n\t\t\tpodSpec: &corev1.PodSpec{Containers: []corev1.Container{{Name: \"foo\"}}},\n\t\t\twant:    []corev1.Container{{Name: \"foo\"}},\n\t\t},\n\t\t{\n\t\t\tname: \"init container\",\n\t\t\tpodSpec: &corev1.PodSpec{\n\t\t\t\tContainers:     []corev1.Container{{Name: \"foo\"}},\n\t\t\t\tInitContainers: []corev1.Container{{Name: \"init\"}},\n\t\t\t},\n\t\t\twant: []corev1.Container{\n\t\t\t\t{Name: \"foo\"},\n\t\t\t\t{Name: \"init\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"init container and sidecar\",\n\t\t\tpodSpec: &corev1.PodSpec{\n\t\t\t\tContainers:     []corev1.Container{{Name: \"proxy\"}, {Name: \"foo\"}},\n\t\t\t\tInitContainers: []corev1.Container{{Name: \"init\"}},\n\t\t\t},\n\t\t\twant: []corev1.Container{\n\t\t\t\t{Name: \"proxy\"},\n\t\t\t\t{Name: \"foo\"},\n\t\t\t\t{Name: \"init\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"ephemeral container\",\n\t\t\tpodSpec: &corev1.PodSpec{\n\t\t\t\tContainers:          []corev1.Container{{Name: \"proxy\"}, {Name: \"foo\"}},\n\t\t\t\tInitContainers:      []corev1.Container{{Name: \"init\"}},\n\t\t\t\tEphemeralContainers: []corev1.EphemeralContainer{{EphemeralContainerCommon: corev1.EphemeralContainerCommon{Name: \"ephemeral\"}}},\n\t\t\t},\n\t\t\twant: []corev1.Container{\n\t\t\t\t{Name: \"proxy\"},\n\t\t\t\t{Name: \"foo\"},\n\t\t\t\t{Name: \"init\"},\n\t\t\t\t{Name: \"ephemeral\"},\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\tassert.Equalf(t, tt.want, extractAllContainers(tt.podSpec), \"extractAllContainers(%v)\", tt.podSpec)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/validator/validator.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage validator\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/google/cel-go/cel\"\n\t\"github.com/google/cel-go/common/types\"\n\t\"github.com/google/cel-go/common/types/ref\"\n\tmarvin \"github.com/undistro/marvin/pkg/types\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"k8s.io/apimachinery/pkg/runtime\"\n\t\"k8s.io/apimachinery/pkg/version\"\n\t\"k8s.io/apiserver/pkg/cel/lazy\"\n)\n\n// CELValidator is a Validator that performs CEL expressions\ntype CELValidator struct {\n\tcheck       marvin.Check\n\tprograms    []cel.Program\n\tapiVersions []string\n\tkubeVersion *version.Info\n\tvariables   []compiledVariable\n}\n\ntype compiledVariable struct {\n\tname    string\n\tprogram cel.Program\n}\n\nfunc (r *CELValidator) SetAPIVersions(apiVersions []string) {\n\tr.apiVersions = apiVersions\n}\n\nfunc (r *CELValidator) SetKubeVersion(v *version.Info) {\n\tr.kubeVersion = v\n}\n\nfunc (r *CELValidator) Validate(obj unstructured.Unstructured, params any) (bool, string, error) {\n\tif params == nil {\n\t\tparams = r.check.Params\n\t}\n\tinput := &activation{object: obj.UnstructuredContent(), apiVersions: r.apiVersions, params: params, variables: make(map[string]any)}\n\tif err := r.setPodSpecParams(obj, input); err != nil {\n\t\treturn false, \"\", err\n\t}\n\tlazyMap := lazy.NewMapValue(types.MapType)\n\tfor _, v := range r.variables {\n\t\tlazyMap.Append(v.name, callback(v, input))\n\t}\n\tinput.variables = lazyMap\n\tfor i, prg := range r.programs {\n\t\tout, _, err := prg.Eval(input)\n\t\tif err != nil {\n\t\t\treturn false, \"\", fmt.Errorf(\"evaluate error: %s\", err)\n\t\t}\n\t\tif out != types.True {\n\t\t\treturn false, r.check.Validations[i].Message, nil\n\t\t}\n\t}\n\treturn true, \"\", nil\n}\n\nfunc callback(v compiledVariable, activation any) lazy.GetFieldFunc {\n\treturn func(_ *lazy.MapValue) ref.Val {\n\t\tval, _, err := v.program.Eval(activation)\n\t\tif err != nil {\n\t\t\treturn types.NewErr(\"variable %q fails to evaluate: %v\", v.name, err)\n\t\t}\n\t\treturn val\n\t}\n}\n\nfunc (r *CELValidator) setPodSpecParams(obj unstructured.Unstructured, input *activation) error {\n\tif !HasPodSpec(obj) {\n\t\treturn nil\n\t}\n\tmeta, spec, err := ExtractPodSpec(obj)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"pod spec extract error: %s\", err)\n\t}\n\tpodSpec, err := runtime.DefaultUnstructuredConverter.ToUnstructured(spec)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"podSpec to unstructured converter error: %s\", err.Error())\n\t}\n\tpodMeta, err := runtime.DefaultUnstructuredConverter.ToUnstructured(meta)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"podMeta to unstructured converter error: %s\", err.Error())\n\t}\n\tinput.podSpec = podSpec\n\tinput.podMeta = podMeta\n\tfor _, container := range extractAllContainers(spec) {\n\t\tc, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&container)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"container to unstructured converter error: %s\", err.Error())\n\t\t}\n\t\tinput.allContainers = append(input.allContainers, c)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/version/version.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage version\n\nimport (\n\t\"github.com/Masterminds/semver/v3\"\n)\n\nvar (\n\tversion = \"dev\"\n\tcommit  = \"\"\n\tdate    = \"\"\n)\n\ntype Info struct {\n\tVersion string `json:\"version\"`\n\tMajor   uint64 `json:\"major\"`\n\tMinor   uint64 `json:\"minor\"`\n\tCommit  string `json:\"commit\"`\n\tDate    string `json:\"date\"`\n}\n\nfunc (i Info) String() string {\n\treturn i.Version\n}\n\nfunc Get() Info {\n\ti := &Info{\n\t\tVersion: version,\n\t\tCommit:  commit,\n\t\tDate:    date,\n\t}\n\tv, err := semver.NewVersion(version)\n\tif err == nil {\n\t\ti.Major = v.Major()\n\t\ti.Minor = v.Minor()\n\t}\n\treturn *i\n}\n"
  },
  {
    "path": "pkg/version/version_test.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage version\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc TestGet(t *testing.T) {\n\ttype args struct {\n\t\tversion string\n\t\tcommit  string\n\t\tdate    string\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs *args\n\t\twant Info\n\t}{\n\t\t{\n\t\t\tname: \"default\",\n\t\t\twant: Info{\n\t\t\t\tVersion: \"dev\",\n\t\t\t\tCommit:  \"\",\n\t\t\t\tDate:    \"\",\n\t\t\t\tMajor:   0,\n\t\t\t\tMinor:   0,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"version\",\n\t\t\targs: &args{\n\t\t\t\tversion: \"0.1.0\",\n\t\t\t\tcommit:  \"commit\",\n\t\t\t\tdate:    \"date\",\n\t\t\t},\n\t\t\twant: Info{\n\t\t\t\tVersion: \"0.1.0\",\n\t\t\t\tMajor:   0,\n\t\t\t\tMinor:   1,\n\t\t\t\tCommit:  \"commit\",\n\t\t\t\tDate:    \"date\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"prefixed\",\n\t\t\targs: &args{\n\t\t\t\tversion: \"v0.1.0\",\n\t\t\t\tcommit:  \"commit\",\n\t\t\t\tdate:    \"date\",\n\t\t\t},\n\t\t\twant: Info{\n\t\t\t\tVersion: \"v0.1.0\",\n\t\t\t\tMajor:   0,\n\t\t\t\tMinor:   1,\n\t\t\t\tCommit:  \"commit\",\n\t\t\t\tDate:    \"date\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"pre-release\",\n\t\t\targs: &args{\n\t\t\t\tversion: \"v0.1.1-next\",\n\t\t\t\tcommit:  \"commit\",\n\t\t\t\tdate:    \"date\",\n\t\t\t},\n\t\t\twant: Info{\n\t\t\t\tVersion: \"v0.1.1-next\",\n\t\t\t\tMajor:   0,\n\t\t\t\tMinor:   1,\n\t\t\t\tCommit:  \"commit\",\n\t\t\t\tDate:    \"date\",\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\tif tt.args != nil {\n\t\t\t\tversion = tt.args.version\n\t\t\t\tcommit = tt.args.commit\n\t\t\t\tdate = tt.args.date\n\t\t\t}\n\t\t\tgot := Get()\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"Get() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t\tif got.String() != tt.want.Version {\n\t\t\t\tt.Errorf(\"String() = %v, want %v\", got.String(), tt.want.Version)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "test/builtins_test.go",
    "content": "// Copyright 2023 Undistro Authors\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.\n\npackage test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured\"\n\t\"sigs.k8s.io/yaml\"\n\n\t\"github.com/undistro/marvin/pkg/loader\"\n\t\"github.com/undistro/marvin/pkg/validator\"\n)\n\nfunc TestBuiltinChecks(t *testing.T) {\n\tchecks, tests, err := loader.LoadChecksAndTests(\"../internal/builtins/\")\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, checks)\n\tassert.GreaterOrEqual(t, len(checks), len(tests))\n\tfor path, checkTests := range tests {\n\t\tt.Run(path, func(t *testing.T) {\n\t\t\tcheck, ok := checks[path]\n\t\t\tassert.True(t, ok)\n\t\t\tassert.NotNil(t, check)\n\t\t\tassert.NotEmpty(t, check.ID)\n\t\t\tv, err := validator.Compile(check, nil, nil, 1000000)\n\t\t\tassert.NoError(t, err)\n\t\t\tassert.NotNil(t, v)\n\t\t\tfor _, tt := range checkTests {\n\t\t\t\tt.Run(tt.Name, func(t *testing.T) {\n\t\t\t\t\tobj, err := parse(tt.Input)\n\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t\tassert.NotNil(t, obj)\n\t\t\t\t\tv.SetAPIVersions(tt.APIVersions)\n\t\t\t\t\tv.SetKubeVersion(tt.KubeVersion)\n\t\t\t\t\tgot, msg, err := v.Validate(obj, tt.Params)\n\t\t\t\t\tassert.NoError(t, err)\n\t\t\t\t\tassert.Equal(t, tt.Pass, got)\n\t\t\t\t\tassert.Equal(t, tt.Message, msg)\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc parse(i string) (unstructured.Unstructured, error) {\n\tvar obj unstructured.Unstructured\n\terr := yaml.Unmarshal([]byte(i), &obj)\n\treturn obj, err\n}\n"
  }
]