File kube-score-1.20.0.obscpio of Package kube-score

07070100000000000081A4000000000000000000000001680FB1F800000199000000000000000000000000000000000000002000000000kube-score-1.20.0/.golangci.yml
linters:
  disable-all: true
  enable:
    - deadcode
    - errcheck
    - gosimple
    - govet
    - ineffassign
    - staticcheck
    - structcheck
    - typecheck
    - unused
    - varcheck
    - bodyclose
    - contextcheck
    - cyclop
    - durationcheck
    - errname
    - errorlint
    - exportloopref
    - goimports
    - gosec
    - gocritic

linters-settings:
  cyclop:
    max-complexity: 35

07070100000001000081A4000000000000000000000001680FB1F800000321000000000000000000000000000000000000002200000000kube-score-1.20.0/.goreleaser.ymlproject_name: kube-score
version: 2

builds:
- env:
  - CGO_ENABLED=0
  goos:
  - linux
  - darwin
  - windows
  goarch:
  - amd64
  - arm
  - arm64
  dir: cmd/kube-score

archives:
  - id: binary
    format: binary
    files:
      - LICENSE

  # A release in archive format is needed for the homebrew release
  - id: default
    files:
      - LICENSE

checksum:
  name_template: 'checksums.txt'

snapshot:
  version_template: "{{ .Tag }}-next"

changelog:
  sort: asc

brews:
  - name: kube-score
    ids:
    - default
    repository:
      owner: kube-score
      name: homebrew-tap
    directory: Formula
    url_template: "https://github.com/zegl/kube-score/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
    homepage: "https://kube-score.com/"
    install: |
      bin.install "kube-score"
07070100000002000081A4000000000000000000000001680FB1F800000A13000000000000000000000000000000000000001D00000000kube-score-1.20.0/Dockerfile# syntax=docker/dockerfile:1

ARG ALPINE_IMAGE=alpine:3.17.0

FROM ${ALPINE_IMAGE} as args
ARG HELM_VERSION=v3.10.2
ARG KUSTOMIZE_VERSION=v4.5.7

FROM args as args-amd64
ARG HELM_SHA256SUM="2315941a13291c277dac9f65e75ead56386440d3907e0540bf157ae70f188347"
ARG KUSTOMIZE_SHA256SUM="701e3c4bfa14e4c520d481fdf7131f902531bfc002cb5062dcf31263a09c70c9"

FROM args as args-arm64
ARG HELM_SHA256SUM="57fa17b6bb040a3788116557a72579f2180ea9620b4ee8a9b7244e5901df02e4"
ARG KUSTOMIZE_SHA256SUM="65665b39297cc73c13918f05bbe8450d17556f0acd16242a339271e14861df67"

# FROM args as args-arm
# ARG HELM_SHA256SUM="25af344f46348958baa1c758cdf3b204ede3ddc483be1171ed3738d47efd0aae"

FROM --platform=$BUILDPLATFORM args-${TARGETARCH} as downloader

ARG TARGETPLATFORM
ARG BUILDPLATFORM
ARG TARGETARCH

RUN apk update && apk add curl

RUN curl --location "https://get.helm.sh/helm-${HELM_VERSION}-linux-${TARGETARCH}.tar.gz" > helm.tar.gz && \
    echo "${HELM_SHA256SUM}  helm.tar.gz" | sha256sum && \
    tar xzvf helm.tar.gz && \
    chmod +x /linux-${TARGETARCH}/helm && \
    mv  /linux-${TARGETARCH}/helm /usr/bin/helm

RUN curl --location "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2F${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_${TARGETARCH}.tar.gz" > kustomize.tar.gz && \
    echo "${KUSTOMIZE_SHA256SUM}  kustomize.tar.gz" | sha256sum && \
    tar xzvf kustomize.tar.gz && \
    chmod +x kustomize && \
    mv kustomize /usr/bin/kustomize

FROM golang:1.23-alpine as builder
ARG TARGETARCH
ARG TARGETPLATFORM
WORKDIR /kube-score
COPY go.mod go.sum ./
RUN go mod download
COPY . .

ARG KUBE_SCORE_VERSION
ARG KUBE_SCORE_COMMIT
ARG KUBE_SCORE_DATE

RUN --mount=type=cache,target=/root/.cache/go-build \
    CGO_ENABLED=0 \
    go build \
    -ldflags="-X 'main.version=${KUBE_SCORE_VERSION}-docker-${TARGETPLATFORM}' -X 'main.commit=${KUBE_SCORE_COMMIT}' -X 'main.date=${KUBE_SCORE_DATE}'" \
    -o /usr/bin/kube-score \
    ./cmd/kube-score

FROM ${ALPINE_IMAGE} as runner
RUN apk update && \
    apk upgrade && \
    apk add bash ca-certificates git

COPY --from=downloader /usr/bin/helm /usr/bin/helm
COPY --from=downloader /usr/bin/kustomize /usr/bin/kustomize
COPY --from=builder /usr/bin/kube-score /usr/bin/kube-score

# Symlink to /kube-score for backwards compatibility (with kube-score v1.15.0 and earlier)
RUN ln -s /usr/bin/kube-score /kube-score

# Dry runs
RUN /kube-score version && \
    /usr/bin/kube-score version && \
    kube-score version && \
    helm version && kustomize version

WORKDIR /project
ENTRYPOINT ["/kube-score"]
07070100000003000081A4000000000000000000000001680FB1F800000430000000000000000000000000000000000000001A00000000kube-score-1.20.0/LICENSEMIT License

Copyright (c) 2018 Gustav Westling

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
07070100000004000081A4000000000000000000000001680FB1F80000224A000000000000000000000000000000000000001C00000000kube-score-1.20.0/README.md# kube-score

<p align="center"><img src="https://user-images.githubusercontent.com/47952/56085330-6c0a2480-5e41-11e9-89ba-0cfddd7714a8.png" height="200"></p>

[![Go Report Card](https://goreportcard.com/badge/github.com/zegl/kube-score?)](https://goreportcard.com/report/github.com/zegl/kube-score)
[![Test Go](https://github.com/zegl/kube-score/actions/workflows/test.yaml/badge.svg)](https://github.com/zegl/kube-score/actions/workflows/test.yaml)
[![Releases](https://img.shields.io/github/release-pre/zegl/kube-score.svg)](https://github.com/zegl/kube-score/releases)
![GitHub stars](https://img.shields.io/github/stars/zegl/kube-score.svg?label=github%20stars)
![Downloads](https://img.shields.io/github/downloads/zegl/kube-score/total.svg)
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/zegl/kube-score/blob/master/LICENSE)

---

`kube-score` is a tool that performs static code analysis of your Kubernetes object definitions.

The output is a list of recommendations of what you can improve to make your application more secure and resilient.

You can test kube-score out in the browser with the [online demo](https://kube-score.com) ([source](https://github.com/kube-score/web)).

## Installation

kube-score is easy to install, and is available from the following sources:

| Distribution                                        | Command / Link                                                                          |
|-----------------------------------------------------|-----------------------------------------------------------------------------------------|
| Pre-built binaries for macOS, Linux, and Windows    | [GitHub releases](https://github.com/zegl/kube-score/releases)                          |
| Docker                                              | `docker pull zegl/kube-score` ([Docker Hub)](https://hub.docker.com/r/zegl/kube-score/) |
| Homebrew  (macOS and Linux)                         | `brew install kube-score`                                                |
| [Krew](https://krew.sigs.k8s.io/) (macOS and Linux) | `kubectl krew install score`                                                            |

## Sponsors

[_Your company here?_ ](https://polar.sh/zegl/subscriptions)

## Checks

For a full list of checks, see [README_CHECKS.md](README_CHECKS.md).

* Container limits (should be set)
* Pod is targeted by a `NetworkPolicy`, both egress and ingress rules are recommended
* Deployments and StatefulSets should have a `PodDisruptionPolicy`
* Deployments and StatefulSets should have host PodAntiAffinity configured
* Container probes, a readiness should be configured, and should not be identical to the liveness probe. Read more in  [README_PROBES.md](README_PROBES.md).
* Container securityContext, run as high number user/group, do not run as root or with privileged root fs. Read more in [README_SECURITYCONTEXT.md](README_SECURITYCONTEXT.md).
* Stable APIs, use a stable API if available (supported: Deployments, StatefulSets, DaemonSet)

## Example output

![](https://user-images.githubusercontent.com/47952/63225706-5b90fe80-c1d3-11e9-8b9d-fad7e723afad.png)

## Usage in CI

`kube-score` can run in your CI/CD environment and will exit with exit code 1 if a critical error has been found.
The trigger level can be changed to warning with the `--exit-one-on-warning` argument.

The input to `kube-score` should be all applications that you deploy to the same namespace for the best result.

### Example with Helm

```bash
helm template my-app | kube-score score -
```

### Example with Kustomize

```bash
kustomize build . | kube-score score -
```

### Example with static YAMLs

```bash
kube-score score my-app/*.yaml
```

```bash
kube-score score my-app/deployment.yaml my-app/service.yaml
```

### Example with an existing cluster

```bash
kubectl api-resources --verbs=list --namespaced -o name \
  | xargs -n1 -I{} bash -c "kubectl get {} --all-namespaces -oyaml && echo ---" \
  | kube-score score -
```

### Example with Docker

```bash
docker run -v $(pwd):/project zegl/kube-score:latest score my-app/*.yaml
```

## Configuration

```
Usage of kube-score:
kube-score [action] --flags

Actions:
	score	Checks all files in the input, and gives them a score and recommendations
	list	Prints a CSV list of all available score checks
	version	Print the version of kube-score
	help	Print this message

Flags for score:
      --disable-ignore-checks-annotations   Set to true to disable the effect of the 'kube-score/ignore' annotations
      --disable-optional-checks-annotations Set to true to disable the effect of the 'kube-score/enable' annotations
      --enable-optional-test strings        Enable an optional test, can be set multiple times
      --exit-one-on-warning                 Exit with code 1 in case of warnings
      --help                                Print help
      --ignore-container-cpu-limit          Disables the requirement of setting a container CPU limit
      --ignore-container-memory-limit       Disables the requirement of setting a container memory limit
      --ignore-test strings                 Disable a test, can be set multiple times
      --kubernetes-version string           Setting the kubernetes-version will affect the checks ran against the manifests. Set this to the version of Kubernetes that you're using in production for the best results. (default "v1.18")
  -o, --output-format string                Set to 'human', 'json', 'ci' or 'sarif'. If set to ci, kube-score will output the program in a format that is easier to parse by other programs. Sarif output allows for easier integration with CI platforms. (default "human")
      --output-version string               Changes the version of the --output-format. The 'json' format has version 'v2' (default) and 'v1' (deprecated, will be removed in v1.7.0). The 'human' and 'ci' formats has only version 'v1' (default). If not explicitly set, the default version for that particular output format will be used.
  -v, --verbose count                       Enable verbose output, can be set multiple times for increased verbosity.
```

### Ignoring a test

Tests can be ignored in the whole run of the program, with the `--ignore-test` flag.

A test can also be ignored on a per-object basis, by adding the annotation `kube-score/ignore` to the object.
The value should be a comma-separated string of the [test IDs](README_CHECKS.md).

Example:

Testing this object will temporarily disable the `service-type` test, which warns against using services of type NodePort.

```yaml
apiVersion: v1
kind: Service
metadata:
  name: node-port-service-with-ignore
  namespace: foospace
  annotations:
    kube-score/ignore: service-type
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: NodePort
```

### Enabling an optional test

Optional tests can be enabled in the whole run of the program, with the `--enable-optional-test` flag.

A test can also be enabled on a per-object basis, by adding the annotation `kube-score/enable` to the object.
The value should be a comma-separated string of the [test IDs](README_CHECKS.md).

Example:

Testing this object will enable the `container-seccomp-profile` test.
Also, multiple tests defined by `kube-score/ignore` are also ignored at the same.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: optional-test-manifest-deployment
  labels:
    app: optional-test-manifest
  annotations:
    kube-score/ignore: pod-networkpolicy,container-resources,container-image-pull-policy,container-security-context-privileged,container-security-context-user-group-id,container-security-context-readonlyrootfilesystem,container-ephemeral-storage-request-and-limit
    kube-score/enable: container-seccomp-profile
spec:
  replicas: 1
  selector:
    matchLabels:
      app: optional-test-manifest
  template:
    metadata:
      labels:
        app: optional-test-manifest
    spec:
      containers:
      - name: optional-test-manifest
        image: busybox:1.34
        command:
        - /bin/sh
        - -c
        - date; env; tail -f /dev/null
```

## Building from source

`kube-score` requires [Go](https://golang.org/) `1.21` or later to build. Clone this repository, and then:

```bash
# Build the project
go build ./cmd/kube-score

# Run all tests
go test -v ./...
```

## Contributing?

Do you want to help out? Take a look at the [Contributing Guidelines](./.github/CONTRIBUTING.md) for more info. 🤩

## Dependencies

| Project             | Version |
|---------------------|---------|
| go.dev              | ^1.21   | 

## Made by

<a href="https://github.com/zegl/kube-score/graphs/contributors">
  <img src="https://contrib.rocks/image?repo=zegl/kube-score" />
</a>
07070100000005000081A4000000000000000000000001680FB1F8000014C4000000000000000000000000000000000000002300000000kube-score-1.20.0/README_CHECKS.md<!-- This file was generated by hack/generate-list-docs.py -->
| ID | Target | Description | Enabled |
|----|--------|-------------|---------|
| deployment-strategy | Deployment | Makes sure that all Deployments targeted by service use RollingUpdate strategy | default |
| deployment-replicas | Deployment | Makes sure that Deployment has multiple replicas. The --min-replicas-deployment flag can be used to specify the required minimum. Default is 2. | default |
| ingress-targets-service | Ingress | Makes sure that the Ingress targets a Service | default |
| cronjob-has-deadline | CronJob | Makes sure that all CronJobs has a configured deadline | default |
| cronjob-restartpolicy | CronJob | Makes sure CronJobs have a valid RestartPolicy | default |
| container-resources | Pod | Makes sure that all pods have resource limits and requests set. The --ignore-container-cpu-limit flag can be used to disable the requirement of having a CPU limit | default |
| container-resource-requests-equal-limits | Pod | Makes sure that all pods have the same requests as limits on resources set. | optional |
| container-cpu-requests-equal-limits | Pod | Makes sure that all pods have the same CPU requests as limits set. | optional |
| container-memory-requests-equal-limits | Pod | Makes sure that all pods have the same memory requests as limits set. | optional |
| container-image-tag | Pod | Makes sure that a explicit non-latest tag is used | default |
| container-image-pull-policy | Pod | Makes sure that the pullPolicy is set to Always. This makes sure that imagePullSecrets are always validated. | default |
| container-ephemeral-storage-request-and-limit | Pod | Makes sure all pods have ephemeral-storage requests and limits set | default |
| container-ephemeral-storage-request-equals-limit | Pod | Make sure all pods have matching ephemeral-storage requests and limits | optional |
| container-ports-check | Pod | Container Ports Checks | optional |
| environment-variable-key-duplication | Pod | Makes sure that duplicated environment variable keys are not duplicated | default |
| statefulset-has-poddisruptionbudget | StatefulSet | Makes sure that all StatefulSets are targeted by a PDB | default |
| deployment-has-poddisruptionbudget | Deployment | Makes sure that all Deployments are targeted by a PDB | default |
| poddisruptionbudget-has-policy | PodDisruptionBudget | Makes sure that PodDisruptionBudgets specify minAvailable or maxUnavailable | default |
| pod-networkpolicy | Pod | Makes sure that all Pods are targeted by a NetworkPolicy | default |
| networkpolicy-targets-pod | NetworkPolicy | Makes sure that all NetworkPolicies targets at least one Pod | default |
| pod-probes | Pod | Makes sure that all Pods have safe probe configurations | default |
| pod-probes-identical | Pod | Makes sure that readiness and liveness probes are not identical | default |
| container-security-context-user-group-id | Pod | Makes sure that all pods have a security context with valid UID and GID set  | default |
| container-security-context-privileged | Pod | Makes sure that all pods have a unprivileged security context set | default |
| container-security-context-readonlyrootfilesystem | Pod | Makes sure that all pods have a security context with read only filesystem set | default |
| container-seccomp-profile | Pod | Makes sure that all pods have at a seccomp policy configured. | optional |
| service-targets-pod | Service | Makes sure that all Services targets a Pod | default |
| service-type | Service | Makes sure that the Service type is not NodePort | default |
| stable-version | all | Checks if the object is using a deprecated apiVersion | default |
| deployment-has-host-podantiaffinity | Deployment | Makes sure that a podAntiAffinity has been set that prevents multiple pods from being scheduled on the same node. https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ | default |
| statefulset-has-host-podantiaffinity | StatefulSet | Makes sure that a podAntiAffinity has been set that prevents multiple pods from being scheduled on the same node. https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ | default |
| deployment-targeted-by-hpa-does-not-have-replicas-configured | Deployment | Makes sure that Deployments using a HorizontalPodAutoscaler doesn't have a statically configured replica count set | default |
| statefulset-has-servicename | StatefulSet | Makes sure that StatefulSets have an existing headless serviceName. | default |
| deployment-pod-selector-labels-match-template-metadata-labels | Deployment | Ensure the StatefulSet selector labels match the template metadata labels. | default |
| statefulset-pod-selector-labels-match-template-metadata-labels | StatefulSet | Ensure the StatefulSet selector labels match the template metadata labels. | default |
| label-values | all | Validates label values | default |
| horizontalpodautoscaler-has-target | HorizontalPodAutoscaler | Makes sure that the HPA targets a valid object | default |
| horizontalpodautoscaler-replicas | HorizontalPodAutoscaler | Makes sure that the HPA has multiple replicas. The --min-replicas-hpa flag can be used to specify the required minimum. Default is 2. | default |
| pod-topology-spread-constraints | Pod | Pod Topology Spread Constraints | default |
07070100000006000081A4000000000000000000000001680FB1F800000DE4000000000000000000000000000000000000002300000000kube-score-1.20.0/README_PROBES.md# Readiness and Liveness Probes

Kubernetes supports three types of probes, that all serve different purposes, and have different pros and cons.

This article is here to describe what recommendations kube-score will make in what situations.

## Different probe types

### `readinessProbe`

This probe roughly answers the question

> Is it a good idea to send traffic to this Pod right now?

A common *misunderstanding* is that, since Kubernetes manages the Pods, you don't need to do graceful draining of Pods during shutdown.

Without a readinessProbe you're risking that:

* Traffic is sent to the Pod before the server has started.
* Traffic is still sent to the Pod _after_ the Pod has stopped. 

**kube-score recommends**:

Every application that is targeted by a Service to:

* Setup a readinessProbe that responds with a healthy status when: The application is fully booted, and ready to serve traffic.
* Set `interval` (default: 10s), `timeout` (default: 1s), `successThreshold` (default: 1), `failureThreshold` (default: 3) to your needs. In the default configuration, your application will fail for 30s (+ the time it takes for the network to react), for clients to stop sending traffic to your application.
* Don't depend on downstream dependencies, such as other services or databases in your probe. If the dependency has a hickup, or for example, a database is restarted, removing your Pods from the Load Balancers rotation will likely only make the downtime worse.
* Handle shutdowns gracefully: Applications should start to fail the readinessProbe after receiving `SIGTERM`, and _wait_ until the service is unregistered from all Load Balancers, before shutting down. As an alternative to this, a [`preStop`](https://kubernetes.io/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/#define-poststart-and-prestop-handlers) hook with a sleep can be used. 

### `livenessProbe`

This probe roughly answers the question

> Is the _container_ healthy right now, or do we need to restart it?

It can be used to let Kubernetes know if your application is deadlocked, and needs to be restarted. Only the container with the failing probe will be restarted, other containers in the same Pod will be unaffected.

**kube-score recommends**:

* If you don't know why you need a livenessProbe, don't configure it.
* It should _never_, be the same as your `readinessProbe`.
* The livenessProbe should *never* depend on downstream dependencies, such as databases or other services.


### `startupProbe` (alpha since v1.16, beta since v1.17)

This probe roughly answers the question

> Should we start running the livenessProbe now?

For applications that take a longer time to boot than the livenessProbes `initialDelaySeconds` + `periodSeconds` * `failureThreshold`, a `startupProbe` can be configured.

The startupProbe allows you to decrease the liveness probes `initialDelaySeconds`, and catch application deadlocks earlier.

As soon as the startupProbe has succeeded once, the livenessProbe will start to be executed.

**kube-score recommends**:

* Configure a startupProbe if you have a livenessProbe configured. 

## Further reading

* [Pod Lifecycle, kubernetes.io](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes)
* [Configure Liveness, Readiness and Startup Probes, kubernetes.io](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)
* [Liveness probes are dangerous, srcco.de](https://srcco.de/posts/kubernetes-liveness-probes-are-dangerous.html)
07070100000007000081A4000000000000000000000001680FB1F8000006E5000000000000000000000000000000000000002C00000000kube-score-1.20.0/README_SECURITYCONTEXT.md# Security Context

The default `container-security-context` check checks the `SecurityContext`
for 

* Containers with writeable root filesystems
* Containers that run with user ID or group ID < 10000
* Privileged containers

If you do not want all of this checks you can disable `container-security-context`
and enable one or more of the following optional checks:

* `container-security-context-user-group-id`
* `container-security-context-privileged`
* `container-security-context-readonlyrootfilesystem`

## Removal timeline of `container-security-context`

`container-security-context` has been deprecated (see [#204](https://github.com/zegl/kube-score/pull/204), [#325](https://github.com/zegl/kube-score/pull/325), [#326](https://github.com/zegl/kube-score/pull/326)).

The checks that has container-security-context preformed has been split into three different checks, which where all introduced in v1.10.

* v1.10: Introduce the three new checks (opt-in), and officially deprecate `container-security-context`.
* v1.12: Make `container-security-context` optional (opt-in), and make the three new checks run by default.
* v1.13: Remove `container-security-context`.

In v1.10, run kube-score with the following flags to ensure compatability with v1.12 and later:

```bash
kube-score score \
    --enable-optional-test container-security-context-user-group-id \
    --enable-optional-test container-security-context-privileged \
    --enable-optional-test container-security-context-readonlyrootfilesystem \
    --ignore-test container-security-context
```

----

_Note:_ The "flip" and the deletion of the tests where originally scheduled to happen in v1.11 and v1.12. This did not happend, and the migration is now scheduled for v1.12 and v1.13 instead.
07070100000008000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001600000000kube-score-1.20.0/cmd07070100000009000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002100000000kube-score-1.20.0/cmd/kube-score0707010000000A000081A4000000000000000000000001680FB1F80000004B000000000000000000000000000000000000002C00000000kube-score-1.20.0/cmd/kube-score/DockerfileFROM scratch
COPY kube-score /
WORKDIR /project
ENTRYPOINT ["/kube-score"]
0707010000000B000081A4000000000000000000000001680FB1F8000004E2000000000000000000000000000000000000002800000000kube-score-1.20.0/cmd/kube-score/cmd.gopackage main

import (
	"fmt"
	"path"
	"strings"
)

type cmdFunc func(string, []string)

func parse(args []string, cmds map[string]cmdFunc) (command string, cmdArgsOffset int, err error) {
	helpName := execName(args[0])

	// When executing kube-score as a kubectl plugin, default to the "score" sub-command to avoid stuttering
	// "kubectl score" is equivalent to "kubectl score score"
	if isKubectlPlugin(helpName) {
		command = "score"
		cmdArgsOffset = 1
	}

	// No command, flag, or file has been specified
	if len(args) <= cmdArgsOffset {
		err = fmt.Errorf("No command, flag or file")
		return
	}

	// If arg 1 is set and is a valid command, always use it as the command to execute, instead of the default
	if len(args) > 1 {
		if _, ok := cmds[args[1]]; ok {
			command = args[1]
			cmdArgsOffset = 2
		}
	}
	return
}

func execName(args0 string) string {
	// Detect name of the binary
	binName := path.Base(args0)

	// If executed as a kubectl plugin, replace dash with a space
	// "kubectl-score" -> "kubectl score"
	if strings.HasPrefix(binName, "kubectl-") {
		binName = strings.Replace(binName, "kubectl-", "kubectl ", 1)
	}

	return binName
}

func isKubectlPlugin(helpName string) bool {
	return execName(helpName) == "kubectl score"
}
0707010000000C000081A4000000000000000000000001680FB1F8000003DD000000000000000000000000000000000000002D00000000kube-score-1.20.0/cmd/kube-score/cmd_test.gopackage main

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestParseCli(t *testing.T) {
	cmds := map[string]cmdFunc{
		"a":     func(string, []string) {},
		"b":     func(string, []string) {},
		"score": func(string, []string) {},
	}

	cmd, offset, err := parse([]string{"kube-score", "a"}, cmds)
	assert.Equal(t, "a", cmd)
	assert.Equal(t, 2, offset)
	assert.Nil(t, err)

	cmd, offset, err = parse([]string{"kube-score", "unknown"}, cmds)
	assert.Equal(t, "", cmd)
	assert.Equal(t, 0, offset)
	assert.Nil(t, err)

	cmd, offset, err = parse([]string{"kubectl-score", "a"}, cmds)
	assert.Equal(t, "a", cmd)
	assert.Equal(t, 2, offset)
	assert.Nil(t, err)

	cmd, offset, err = parse([]string{"kubectl-score", "xyz.yaml"}, cmds)
	assert.Equal(t, "score", cmd)
	assert.Equal(t, 1, offset)
	assert.Nil(t, err)

	cmd, offset, err = parse([]string{"kubectl-score", "score", "xyz.yaml"}, cmds)
	assert.Equal(t, "score", cmd)
	assert.Equal(t, 2, offset)
	assert.Nil(t, err)
}
0707010000000D000081A4000000000000000000000001680FB1F8000000FF000000000000000000000000000000000000003000000000kube-score-1.20.0/cmd/kube-score/cmd_version.gopackage main

import (
	"fmt"
)

// These variables are overwritten by goreleaser
var version = "development"
var commit = "N/A"
var date = "N/A"

func cmdVersion() {
	fmt.Printf("kube-score version: %s, commit: %s, built: %s\n", version, commit, date)
}
0707010000000E000081A4000000000000000000000001680FB1F80000026A000000000000000000000000000000000000003100000000kube-score-1.20.0/cmd/kube-score/helm.DockerfileFROM debian:stretch as downloader

ARG HELM_VERSION=v2.17.0
ARG HELM_SHA256SUM="f3bec3c7c55f6a9eb9e6586b8c503f370af92fe987fcbf741f37707606d70296"

RUN apt-get update && \
    apt-get install -y curl && \
    curl --location "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz" > helm.tar.gz && \
    echo "${HELM_SHA256SUM}  helm.tar.gz" | sha256sum --check && \
    tar xzvf helm.tar.gz && \
    chmod +x /linux-amd64/helm

FROM alpine:3.14.2
RUN apk update && \
    apk upgrade && \
    apk add bash ca-certificates
COPY --from=downloader /linux-amd64/helm /usr/bin/helm
COPY kube-score /usr/bin/kube-score
0707010000000F000081A4000000000000000000000001680FB1F800000269000000000000000000000000000000000000003200000000kube-score-1.20.0/cmd/kube-score/helm3.DockerfileFROM debian:stretch as downloader

ARG HELM_VERSION=v3.7.0
ARG HELM_SHA256SUM="096e30f54c3ccdabe30a8093f8e128dba76bb67af697b85db6ed0453a2701bf9"

RUN apt-get update && \
    apt-get install -y curl && \
    curl --location "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz" > helm.tar.gz && \
    echo "${HELM_SHA256SUM}  helm.tar.gz" | sha256sum --check && \
    tar xzvf helm.tar.gz && \
    chmod +x /linux-amd64/helm

FROM alpine:3.14.2
RUN apk update && \
    apk upgrade && \
    apk add bash ca-certificates
COPY --from=downloader /linux-amd64/helm /usr/bin/helm
COPY kube-score /usr/bin/kube-score
07070100000010000081A4000000000000000000000001680FB1F80000010B000000000000000000000000000000000000002E00000000kube-score-1.20.0/cmd/kube-score/help_test.gopackage main

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestExecName(t *testing.T) {
	assert.Equal(t, "kube-score", execName("kube-score"))
	assert.Equal(t, "ks", execName("ks"))
	assert.Equal(t, "kubectl score", execName("kubectl-score"))
}
07070100000011000081A4000000000000000000000001680FB1F8000002D6000000000000000000000000000000000000003600000000kube-score-1.20.0/cmd/kube-score/kustomize.DockerfileFROM debian:stretch as downloader

ARG KUSTOMIZE_VERSION=v4.5.4
ARG KUSTOMIZE_SHA256SUM="1159c5c17c964257123b10e7d8864e9fe7f9a580d4124a388e746e4003added3"

RUN apt-get update && \
    apt-get install -y curl && \
    curl --location "https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2F${KUSTOMIZE_VERSION}/kustomize_${KUSTOMIZE_VERSION}_linux_amd64.tar.gz" > kustomize.tar.gz && \
    echo "${KUSTOMIZE_SHA256SUM}  kustomize.tar.gz" | sha256sum --check && \
    tar xzvf kustomize.tar.gz && \
    chmod +x kustomize

FROM alpine:3.10.1
RUN apk update && \
    apk upgrade && \
    apk add bash ca-certificates git
COPY --from=downloader kustomize /usr/bin/kustomize
COPY kube-score /usr/bin/kube-score
07070100000012000081A4000000000000000000000001680FB1F800002BCE000000000000000000000000000000000000002900000000kube-score-1.20.0/cmd/kube-score/main.gopackage main

import (
	"bytes"
	"encoding/csv"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/mattn/go-isatty"
	flag "github.com/spf13/pflag"
	"github.com/zegl/kube-score/config"
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/parser"
	"github.com/zegl/kube-score/renderer/ci"
	"github.com/zegl/kube-score/renderer/human"
	"github.com/zegl/kube-score/renderer/json_v2"
	"github.com/zegl/kube-score/renderer/junit"
	"github.com/zegl/kube-score/renderer/sarif"
	"github.com/zegl/kube-score/score"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"
	"golang.org/x/term"
)

func main() {
	helpName := execName(os.Args[0])

	fs := flag.NewFlagSet(helpName, flag.ExitOnError)
	setDefault(fs, helpName, "", true)

	cmds := map[string]cmdFunc{
		"score": func(helpName string, args []string) {
			if err := scoreFiles(helpName, args); err != nil {
				_, _ = fmt.Fprintf(os.Stderr, "Failed to score files: %v\n", err)
				os.Exit(1)
			}
		},

		"list": func(helpName string, args []string) {
			if err := listChecks(helpName, args); err != nil {
				_, _ = fmt.Fprintf(os.Stderr, "Failed to list checks: %v\n", err)
				os.Exit(1)
			}
		},

		"version": func(helpName string, args []string) {
			cmdVersion()
		},

		"help": func(helpName string, args []string) {
			fs.Usage()
			os.Exit(1)
		},
	}

	command, cmdArgsOffset, err := parse(os.Args, cmds)
	if err != nil {
		fs.Usage()
		os.Exit(1)
	}

	// Execute the command, or the help command if no matching command is found
	if ex, ok := cmds[command]; ok {
		ex(helpName, os.Args[cmdArgsOffset:])
	} else {
		cmds["help"](helpName, os.Args[cmdArgsOffset:])
	}
}

func setDefault(fs *flag.FlagSet, binName, actionName string, displayForMoreInfo bool) {
	fs.Usage = func() {
		usage := fmt.Sprintf(`Usage of %s:
%s [action] --flags

Actions:
	score	Checks all files in the input, and gives them a score and recommendations
	list	Prints a CSV list of all available score checks
	version	Print the version of kube-score
	help	Print this message`+"\n\n", binName, binName)

		if displayForMoreInfo {
			usage += fmt.Sprintf(`Run "%s [action] --help" for more information about a particular command`, binName)
		}

		if len(actionName) > 0 {
			usage += "Flags for " + actionName + ":"
		}

		fmt.Println(usage)

		if len(actionName) > 0 {
			fs.PrintDefaults()
		}
	}
}

func scoreFiles(binName string, args []string) error {
	fs := flag.NewFlagSet(binName, flag.ExitOnError)
	exitOneOnWarning := fs.Bool("exit-one-on-warning", false, "Exit with code 1 in case of warnings")
	ignoreContainerCpuLimit := fs.Bool("ignore-container-cpu-limit", false, "Disables the requirement of setting a container CPU limit")
	ignoreContainerMemoryLimit := fs.Bool("ignore-container-memory-limit", false, "Disables the requirement of setting a container memory limit")
	verboseOutput := fs.CountP("verbose", "v", "Enable verbose output, can be set multiple times for increased verbosity.")
	printHelp := fs.Bool("help", false, "Print help")
	outputFormat := fs.StringP("output-format", "o", "human", "Set to 'human', 'json', 'ci' or 'sarif'. If set to ci, kube-score will output the program in a format that is easier to parse by other programs. Sarif output allows for easier integration with CI platforms.")
	outputVersion := fs.String("output-version", "", "Changes the version of the --output-format. The 'json' format has version 'v2' (default) and 'v1' (deprecated, will be removed in v1.7.0). The 'human' and 'ci' formats has only version 'v1' (default). If not explicitly set, the default version for that particular output format will be used.")
	color := fs.String("color", "auto", "If the output should be colored. Set to 'always', 'never' or 'auto'. If set to 'auto', kube-score will try to detect if the current terminal / platform supports colors. If set to 'never', kube-score will not output any colors. If set to 'always', kube-score will output colors even if the current terminal / platform does not support colors.")
	optionalTests := fs.StringSlice("enable-optional-test", []string{}, "Enable an optional test, can be set multiple times")
	ignoreTests := fs.StringSlice("ignore-test", []string{}, "Disable a test, can be set multiple times")
	disableIgnoreChecksAnnotation := fs.Bool("disable-ignore-checks-annotations", false, "Set to true to disable the effect of the 'kube-score/ignore' annotations")
	disableOptionalChecksAnnotation := fs.Bool("disable-optional-checks-annotations", false, "Set to true to disable the effect of the 'kube-score/enable' annotations")
	allDefaultOptional := fs.Bool("all-default-optional", false, "Set to true to enable all tests")
	kubernetesVersion := fs.String("kubernetes-version", "v1.18", "Setting the kubernetes-version will affect the checks ran against the manifests. Set this to the version of Kubernetes that you're using in production for the best results.")
	minReplicasDeployment := fs.Int("min-replicas-deployment", 2, "Minimum required number of replicas for a deployment")
	minReplicasHPA := fs.Int("min-replicas-hpa", 2, "Minimum required number of replicas for a horizontal pod autoscaler")
	setDefault(fs, binName, "score", false)

	err := fs.Parse(args)
	if err != nil {
		return fmt.Errorf("failed to parse files: %w", err)
	}

	if *printHelp {
		fs.Usage()
		return nil
	}

	if *outputFormat != "human" && *outputFormat != "ci" && *outputFormat != "json" && *outputFormat != "sarif" && *outputFormat != "junit" {
		fs.Usage()
		return fmt.Errorf("Error: --output-format must be set to: 'human', 'json', 'sarif', 'junit' or 'ci'")
	}

	acceptedColors := map[string]bool{
		"auto":   true,
		"always": true,
		"never":  true,
	}
	if !acceptedColors[*color] {
		fs.Usage()
		return fmt.Errorf("Error: --color must be set to: 'auto', 'always' or 'never'")
	}

	filesToRead := fs.Args()
	if len(filesToRead) == 0 {
		return fmt.Errorf(`Error: No files given as arguments.

Usage: %s score [--flag1 --flag2] file1 file2 ...

Use "-" as filename to read from STDIN.`, execName(binName))
	}

	var allFilePointers []ks.NamedReader

	for _, file := range filesToRead {
		var fp io.Reader
		var filename string

		if file == "-" {
			fp = os.Stdin
			filename = "STDIN"
		} else {
			var err error
			fp, err = os.Open(file)
			if err != nil {
				return err
			}
			filename, _ = filepath.Abs(file)
		}
		allFilePointers = append(allFilePointers, namedReader{Reader: fp, name: filename})
	}

	if len(*ignoreTests) > 0 && *allDefaultOptional {
		return errors.New("Invalid argument combination. --all-default-optional and --ignore-tests cannot be used together")
	}

	if *allDefaultOptional {
		var addOptionalChecks []string
		for _, c := range score.RegisterAllChecks(parser.Empty(), nil, nil).All() {
			if c.Optional {
				addOptionalChecks = append(addOptionalChecks, c.ID)
			}
		}
		optionalTests = &addOptionalChecks
	}

	ignoredTests := listToStructMap(ignoreTests)
	enabledOptionalTests := listToStructMap(optionalTests)

	kubeVer, err := config.ParseSemver(*kubernetesVersion)
	if err != nil {
		return errors.New("Invalid --kubernetes-version. Use on format \"vN.NN\"")
	}

	runConfig := &config.RunConfiguration{
		IgnoreContainerCpuLimitRequirement:    *ignoreContainerCpuLimit,
		IgnoreContainerMemoryLimitRequirement: *ignoreContainerMemoryLimit,
		EnabledOptionalTests:                  enabledOptionalTests,
		UseIgnoreChecksAnnotation:             !*disableIgnoreChecksAnnotation,
		UseOptionalChecksAnnotation:           !*disableOptionalChecksAnnotation,
		KubernetesVersion:                     kubeVer,
		MinReplicasDeployment:                 *minReplicasDeployment,
		MinReplicasHPA:                        *minReplicasHPA,
	}

	p, err := parser.New(&parser.Config{
		VerboseOutput: *verboseOutput,
	})
	if err != nil {
		return fmt.Errorf("failed to initializer parser: %w", err)
	}

	parsedFiles, err := p.ParseFiles(allFilePointers)
	if err != nil {
		return fmt.Errorf("failed to parse files: %w", err)
	}

	checks := score.RegisterAllChecks(parsedFiles, &checks.Config{IgnoredTests: ignoredTests}, runConfig)

	scoreCard, err := score.Score(parsedFiles, checks, runConfig)
	if err != nil {
		return err
	}

	var exitCode int
	switch {
	case scoreCard.AnyBelowOrEqualToGrade(scorecard.GradeCritical):
		exitCode = 1
	case *exitOneOnWarning && scoreCard.AnyBelowOrEqualToGrade(scorecard.GradeWarning):
		exitCode = 1
	default:
		exitCode = 0
	}

	var r io.Reader

	version := getOutputVersion(*outputVersion, *outputFormat)

	switch {
	case *outputFormat == "json" && version == "v1":
		d, _ := json.MarshalIndent(scoreCard, "", "    ")
		w := bytes.NewBufferString("")
		w.WriteString(string(d))
		r = w
	case *outputFormat == "json" && version == "v2":
		r = json_v2.Output(scoreCard)
	case *outputFormat == "human" && version == "v1":
		termWidth, _, err := term.GetSize(int(os.Stdin.Fd()))
		// Assume a width of 80 if it can't be detected
		if err != nil {
			termWidth = 80
		}
		r, err = human.Human(scoreCard, *verboseOutput, termWidth, useColor(*color))
		if err != nil {
			return err
		}
	case *outputFormat == "ci" && version == "v1":
		r = ci.CI(scoreCard)
	case *outputFormat == "sarif":
		r = sarif.Output(scoreCard)
	case *outputFormat == "junit":
		r = junit.JUnit(scoreCard)
	default:
		return fmt.Errorf("error: Unknown --output-format or --output-version")
	}

	output, _ := io.ReadAll(r)
	fmt.Print(string(output))
	os.Exit(exitCode)
	return nil
}

func getOutputVersion(flagValue, format string) string {
	if len(flagValue) > 0 {
		return flagValue
	}

	switch format {
	case "json":
		return "v2"
	default:
		return "v1"
	}
}

func listChecks(binName string, args []string) error {
	fs := flag.NewFlagSet(binName, flag.ExitOnError)
	printHelp := fs.Bool("help", false, "Print help")
	setDefault(fs, binName, "list", false)
	err := fs.Parse(args)
	if err != nil {
		return nil
	}

	if *printHelp {
		fs.Usage()
		return nil
	}

	allChecks := score.RegisterAllChecks(parser.Empty(), nil, nil)

	output := csv.NewWriter(os.Stdout)
	for _, c := range allChecks.All() {
		optionalString := "default"
		if c.Optional {
			optionalString = "optional"
		}
		err := output.Write([]string{c.ID, c.TargetType, c.Comment, optionalString})
		if err != nil {
			return nil
		}
	}
	output.Flush()

	return nil
}

func listToStructMap(items *[]string) map[string]struct{} {
	structMap := make(map[string]struct{})
	for _, testID := range *items {
		structMap[testID] = struct{}{}
	}
	return structMap
}

type namedReader struct {
	io.Reader
	name string
}

func (n namedReader) Name() string {
	return n.name
}

func useColor(colorArg string) bool {
	// Respect user preference
	switch colorArg {
	case "always":
		return true
	case "never":
		return false
	}

	// If running on Github Actions, use colors
	if _, ok := os.LookupEnv("GITHUB_ACTIONS"); ok {
		return true
	}

	// If NO_COLOR is set, don't use color
	if _, ok := os.LookupEnv("NO_COLOR"); ok {
		return false
	}

	// Dont use color if not a terminal
	if os.Getenv("TERM") == "dumb" {
		return false
	}
	if !isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()) {
		return false
	}

	// Use colors
	return true
}
07070100000013000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001B00000000kube-score-1.20.0/cmd/wasm07070100000014000081A4000000000000000000000001680FB1F8000009D1000000000000000000000000000000000000002300000000kube-score-1.20.0/cmd/wasm/main.go//go:build wasm
// +build wasm

package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"strings"
	"syscall/js"

	t2html "github.com/buildkite/terminal-to-html"

	"github.com/zegl/kube-score/config"
	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/parser"
	"github.com/zegl/kube-score/renderer/ci"
	"github.com/zegl/kube-score/renderer/human"
	"github.com/zegl/kube-score/renderer/json_v2"
	"github.com/zegl/kube-score/renderer/junit"
	"github.com/zegl/kube-score/renderer/sarif"
	"github.com/zegl/kube-score/score"
	"github.com/zegl/kube-score/score/checks"
	"golang.org/x/term"
)

func main() {
	js.Global().Set("handleScore", js.FuncOf(handleScore))
	select {}
}

type inputReader struct {
	*strings.Reader
}

func (inputReader) Name() string {
	return "input"
}

func handleScore(this js.Value, inputs []js.Value) interface{} {
	if len(inputs) != 2 {
		fmt.Println("Unexpected number of arguments")
		return "Unexpected number of arguments"
	}

	inputYaml := inputs[0].String()
	format := inputs[1].String()

	reader := &inputReader{
		Reader: strings.NewReader(inputYaml),
	}

	files := []domain.NamedReader{reader}

	p, err := parser.New(&parser.Config{})
	if err != nil {
		fmt.Println(err)
		return string(err.Error())
	}

	allObjs, err := p.ParseFiles(files)
	if err != nil {
		fmt.Println(err)
		return string(err.Error())
	}

	checksConfig := &checks.Config{}
	runConfig := &config.RunConfiguration{}

	allChecks := score.RegisterAllChecks(allObjs, checksConfig, runConfig)

	scoreCard, err := score.Score(allObjs, allChecks, runConfig)
	if err != nil {
		fmt.Println(err)
		return string(err.Error())
	}

	var r io.Reader
	switch format {
	case "json":
		r = json_v2.Output(scoreCard)
	case "human":
		termWidth, _, err := term.GetSize(int(os.Stdin.Fd()))
		// Assume a width of 80 if it can't be detected
		if err != nil {
			termWidth = 80
		}
		body, err := human.Human(scoreCard, 0, termWidth, true)
		if err != nil {
			fmt.Println(err)
			return string(err.Error())
		}

		bodyBytes, err := io.ReadAll(body)
		if err != nil {
			fmt.Println(err)
			return string(err.Error())
		}

		htmlBody := t2html.Render(bodyBytes)
		r = bytes.NewReader(htmlBody)
	case "ci":
		r = ci.CI(scoreCard)
	case "sarif":
		r = sarif.Output(scoreCard)
	case "junit":
		r = junit.JUnit(scoreCard)
	default:
		return fmt.Errorf("error: unknown format")
	}

	body, err := io.ReadAll(r)
	fmt.Println("body", body)
	if err != nil {
		fmt.Println(err)
		return string(err.Error())
	}

	return string(body)
}
07070100000015000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001900000000kube-score-1.20.0/config07070100000016000081A4000000000000000000000001680FB1F800000543000000000000000000000000000000000000002300000000kube-score-1.20.0/config/config.gopackage config

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

type RunConfiguration struct {
	IgnoreContainerCpuLimitRequirement    bool
	IgnoreContainerMemoryLimitRequirement bool
	EnabledOptionalTests                  map[string]struct{}
	UseIgnoreChecksAnnotation             bool
	UseOptionalChecksAnnotation           bool
	KubernetesVersion                     Semver
	MinReplicasDeployment                 int
	MinReplicasHPA                        int
}

type Semver struct {
	Major int
	Minor int
}

var errInvalidSemver = errors.New("invalid semver")

func ParseSemver(s string) (Semver, error) {
	if len(s) == 0 {
		return Semver{}, errInvalidSemver
	}
	start := 0
	if s[0] == 'v' {
		start = 1
	}

	// Separate by .
	parts := strings.Split(s[start:], ".")
	if len(parts) != 2 {
		return Semver{}, errInvalidSemver
	}

	major, err := strconv.Atoi(parts[0])
	if err != nil {
		return Semver{}, errInvalidSemver
	}

	minor, err := strconv.Atoi(parts[1])
	if err != nil {
		return Semver{}, errInvalidSemver
	}

	return Semver{
		Major: major,
		Minor: minor,
	}, nil
}

func (s Semver) LessThan(other Semver) bool {
	if s.Major < other.Major {
		return true
	}
	if s.Major == other.Major && s.Minor < other.Minor {
		return true
	}
	return false
}

func (s Semver) String() string {
	return fmt.Sprintf("v%d.%d", s.Major, s.Minor)
}
07070100000017000081A4000000000000000000000001680FB1F80000045A000000000000000000000000000000000000002800000000kube-score-1.20.0/config/semver_test.gopackage config

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestSemver(t *testing.T) {
	tc := []struct {
		input       string
		expected    Semver
		expectedErr error
	}{
		{"v1.0", Semver{1, 0}, nil},
		{"v1.999", Semver{1, 999}, nil},
		{"1.0", Semver{1, 0}, nil},
		{"1.999", Semver{1, 999}, nil},

		{"foo", Semver{}, errInvalidSemver},
		{"v1.2.3", Semver{}, errInvalidSemver},
		{"v1.foo", Semver{}, errInvalidSemver},
		{"x1.0", Semver{}, errInvalidSemver},
		{"v0x00.123", Semver{}, errInvalidSemver},
		{"v1b.5nn3", Semver{}, errInvalidSemver},
	}

	for d, tc := range tc {
		s, e := ParseSemver(tc.input)
		assert.Equal(t, tc.expected, s, "Case: %d", d)
		assert.Equal(t, tc.expectedErr, e, "Case: %d", d)
	}
}

func TestSemverLessThan(t *testing.T) {
	tc := []struct {
		a        Semver
		b        Semver
		expected bool
	}{
		{Semver{1, 0}, Semver{1, 0}, false},
		{Semver{1, 0}, Semver{1, 2}, true},
		{Semver{1, 6}, Semver{2, 0}, true},
		{Semver{2, 6}, Semver{1, 18}, false},
	}
	for d, tc := range tc {
		assert.Equal(t, tc.expected, tc.a.LessThan(tc.b), "Case: %d", d)
	}
}
07070100000018000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001900000000kube-score-1.20.0/domain07070100000019000081A4000000000000000000000001680FB1F800000AEB000000000000000000000000000000000000002700000000kube-score-1.20.0/domain/kube-score.gopackage domain

import (
	"io"

	autoscalingv1 "k8s.io/api/autoscaling/v1"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	networkingv1 "k8s.io/api/networking/v1"
	policyv1 "k8s.io/api/policy/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type Check struct {
	Name       string
	ID         string
	TargetType string
	Comment    string
	Optional   bool
}

type NamedReader interface {
	io.Reader
	Name() string
}

type FileLocation struct {
	Name string
	Line int
}

type BothMeta struct {
	TypeMeta   metav1.TypeMeta
	ObjectMeta metav1.ObjectMeta
	FileLocationer
}

type PodSpecer interface {
	FileLocationer
	GetTypeMeta() metav1.TypeMeta
	GetObjectMeta() metav1.ObjectMeta
	GetPodTemplateSpec() corev1.PodTemplateSpec
}

type FileLocationer interface {
	FileLocation() FileLocation
}

type HpaTargeter interface {
	GetTypeMeta() metav1.TypeMeta
	GetObjectMeta() metav1.ObjectMeta
	MinReplicas() *int32
	HpaTarget() autoscalingv1.CrossVersionObjectReference
	FileLocationer
}

type Ingress interface {
	GetTypeMeta() metav1.TypeMeta
	GetObjectMeta() metav1.ObjectMeta
	Rules() []networkingv1.IngressRule
	FileLocationer
}

type Metas interface {
	Metas() []BothMeta
}

type Pod interface {
	Pod() corev1.Pod
	FileLocationer
}

type Pods interface {
	Pods() []Pod
}

type PodSpeccers interface {
	PodSpeccers() []PodSpecer
}

type Service interface {
	Service() corev1.Service
	FileLocationer
}

type Services interface {
	Services() []Service
}

type StatefulSet interface {
	StatefulSet() appsv1.StatefulSet
	FileLocationer
}

type StatefulSets interface {
	StatefulSets() []StatefulSet
}

type Deployment interface {
	Deployment() appsv1.Deployment
	FileLocationer
}

type Deployments interface {
	Deployments() []Deployment
}

type NetworkPolicy interface {
	NetworkPolicy() networkingv1.NetworkPolicy
	FileLocationer
}

type NetworkPolicies interface {
	NetworkPolicies() []NetworkPolicy
}

type Ingresses interface {
	Ingresses() []Ingress
}

type CronJob interface {
	GetTypeMeta() metav1.TypeMeta
	GetObjectMeta() metav1.ObjectMeta
	StartingDeadlineSeconds() *int64
	GetPodTemplateSpec() corev1.PodTemplateSpec
	FileLocationer
}

type CronJobs interface {
	CronJobs() []CronJob
}

type PodDisruptionBudget interface {
	GetTypeMeta() metav1.TypeMeta
	GetObjectMeta() metav1.ObjectMeta
	Namespace() string
	Spec() policyv1.PodDisruptionBudgetSpec
	PodDisruptionBudgetSelector() *metav1.LabelSelector
	FileLocationer
}

type PodDisruptionBudgets interface {
	PodDisruptionBudgets() []PodDisruptionBudget
}

type HorizontalPodAutoscalers interface {
	HorizontalPodAutoscalers() []HpaTargeter
}

type AllTypes interface {
	Metas
	Pods
	PodSpeccers
	Services
	StatefulSets
	Deployments
	NetworkPolicies
	Ingresses
	CronJobs
	PodDisruptionBudgets
	HorizontalPodAutoscalers
}
0707010000001A000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001B00000000kube-score-1.20.0/examples0707010000001B000081A4000000000000000000000001680FB1F800000618000000000000000000000000000000000000002B00000000kube-score-1.20.0/examples/custom_check.gopackage examples

import (
	"bytes"
	"io"
	"strings"

	"github.com/zegl/kube-score/config"
	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/parser"
	"github.com/zegl/kube-score/score"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"

	v1 "k8s.io/api/apps/v1"
)

type namedReader struct {
	io.Reader
	name string
}

func (n namedReader) Name() string {
	return n.name
}

// ExampleCheckObject shows how kube-score can be extended with a custom check function
//
// In this example, raw is a YAML encoded Kubernetes object
func ExampleCheckObject(raw []byte) (*scorecard.Scorecard, error) {
	parser, err := parser.New(nil)
	if err != nil {
		return nil, err
	}

	reader := bytes.NewReader(raw)

	// Parse all objects to read
	allObjects, err := parser.ParseFiles(
		[]domain.NamedReader{
			namedReader{
				Reader: reader,
				name:   "input",
			},
		},
	)
	if err != nil {
		return nil, err
	}

	// Register check functions to run
	checks := checks.New(nil)
	checks.RegisterDeploymentCheck("custom-deployment-check", "A custom kube-score check function", customDeploymentCheck)

	return score.Score(allObjects, checks, &config.RunConfiguration{})
}

func customDeploymentCheck(d v1.Deployment) (scorecard.TestScore, error) {
	if strings.Contains(d.Name, "foo") {
		return scorecard.TestScore{
			Grade: scorecard.GradeCritical,
			Comments: []scorecard.TestScoreComment{{
				Summary: "Deployments names can not contian 'foo'",
			}}}, nil
	}

	return scorecard.TestScore{Grade: scorecard.GradeAllOK}, nil
}
0707010000001C000081A4000000000000000000000001680FB1F8000004F7000000000000000000000000000000000000003000000000kube-score-1.20.0/examples/custom_check_test.gopackage examples

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/zegl/kube-score/scorecard"
)

func TestExampleCheckObjectAllOK(t *testing.T) {
	card, err := ExampleCheckObject([]byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
    name: example
spec:
    replicas: 10
    template:
    metadata:
        labels:
            app: foo
    spec:
        containers:
        - name: foobar
          image: foo:bar`))

	assert.NoError(t, err)

	assert.Len(t, *card, 1)

	for _, v := range *card {
		assert.Len(t, v.Checks, 1)
		assert.Equal(t, "custom-deployment-check", v.Checks[0].Check.ID)
		assert.Equal(t, scorecard.GradeAllOK, v.Checks[0].Grade)
	}
}

func TestExampleCheckObjectErrorNameContainsFoo(t *testing.T) {
	card, err := ExampleCheckObject([]byte(`
apiVersion: apps/v1
kind: Deployment
metadata:
    name: example-foo
spec:
    replicas: 10
    template:
    metadata:
        labels:
            app: foo
    spec:
        containers:
        - name: foobar
          image: foo:bar`))

	assert.NoError(t, err)

	assert.Len(t, *card, 1)

	for _, v := range *card {
		assert.Len(t, v.Checks, 1)
		assert.Equal(t, "custom-deployment-check", v.Checks[0].Check.ID)
		assert.Equal(t, scorecard.GradeCritical, v.Checks[0].Grade)
	}
}
0707010000001D000081A4000000000000000000000001680FB1F80000062C000000000000000000000000000000000000001900000000kube-score-1.20.0/go.modmodule github.com/zegl/kube-score

require (
	github.com/buildkite/terminal-to-html v3.2.0+incompatible
	github.com/eidolon/wordwrap v0.0.0-20161011182207-e0f54129b8bb
	github.com/fatih/color v1.18.0
	github.com/google/go-cmp v0.7.0
	github.com/jstemmer/go-junit-report/v2 v2.1.0
	github.com/mattn/go-isatty v0.0.20
	github.com/spf13/pflag v1.0.6
	github.com/stretchr/testify v1.10.0
	golang.org/x/term v0.31.0
	gopkg.in/yaml.v3 v3.0.1
	k8s.io/api v0.32.3
	k8s.io/apimachinery v0.32.3
	k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
)

require (
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
	github.com/fxamacker/cbor/v2 v2.7.0 // indirect
	github.com/go-logr/logr v1.4.2 // indirect
	github.com/gogo/protobuf v1.3.2 // indirect
	github.com/google/gofuzz v1.2.0 // indirect
	github.com/json-iterator/go v1.1.12 // indirect
	github.com/kr/text v0.2.0 // indirect
	github.com/mattn/go-colorable v0.1.13 // indirect
	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
	github.com/modern-go/reflect2 v1.0.2 // indirect
	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
	github.com/x448/float16 v0.8.4 // indirect
	golang.org/x/net v0.36.0 // indirect
	golang.org/x/sys v0.32.0 // indirect
	golang.org/x/text v0.22.0 // indirect
	gopkg.in/inf.v0 v0.9.1 // indirect
	k8s.io/klog/v2 v2.130.1 // indirect
	sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
	sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
	sigs.k8s.io/yaml v1.4.0 // indirect
)

go 1.23.0

toolchain go1.23.1
0707010000001E000081A4000000000000000000000001680FB1F80000298A000000000000000000000000000000000000001900000000kube-score-1.20.0/go.sumgithub.com/buildkite/terminal-to-html v3.2.0+incompatible h1:WdXzl7ZmYzCAz4pElZosPaUlRTW+qwVx/SkQSCa1jXs=
github.com/buildkite/terminal-to-html v3.2.0+incompatible/go.mod h1:BFFdFecOxCgjdcarqI+8izs6v85CU/1RA/4Bqh4GR7E=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/eidolon/wordwrap v0.0.0-20161011182207-e0f54129b8bb h1:ioQwBmKdOCpMVS/bDaESqNWXIE/aw4+gsVtysCGMWZ4=
github.com/eidolon/wordwrap v0.0.0-20161011182207-e0f54129b8bb/go.mod h1:ZAPs+OyRzeVJFGvXVDVffgCzQfjg3qU9Ig8G/MU3zZ4=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report/v2 v2.1.0 h1:X3+hPYlSczH9IMIpSC9CQSZA0L+BipYafciZUWHEmsc=
github.com/jstemmer/go-junit-report/v2 v2.1.0/go.mod h1:mgHVr7VUo5Tn8OLVr1cKnLuEy0M92wdRntM99h7RkgQ=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
0707010000001F000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001700000000kube-score-1.20.0/hack07070100000020000081A4000000000000000000000001680FB1F8000000F4000000000000000000000000000000000000002200000000kube-score-1.20.0/hack/RELEASE.md# How to make a release

1. Build a release `GITHUB_TOKEN=ghp_XXXX goreleaser`
2. Build and push to Docker Hub: `PUSH_LATEST=1 ./hack/release-docker.sh`
3. Update the krew index: `./hack/krew-index.sh`
4. Update the relnotes
5. Update homebrew
07070100000021000081A4000000000000000000000001680FB1F80000016C000000000000000000000000000000000000002D00000000kube-score-1.20.0/hack/generate-list-docs.pyimport subprocess
import csv
from io import StringIO

print("<!-- This file was generated by hack/generate-list-docs.py -->")

print("| ID | Target | Description | Enabled |")
print("|----|--------|-------------|---------|")

listAsCSV = subprocess.getoutput("kube-score list")

for row in csv.reader(StringIO(listAsCSV)):
    print("| " + " | ".join(row) + " |")
07070100000022000081ED000000000000000000000001680FB1F8000005D3000000000000000000000000000000000000002500000000kube-score-1.20.0/hack/krew-index.sh#!/bin/bash

set -euo pipefail
set -x

VERSION=$(git describe --tags --abbrev=0 | cut -c2-)
KREW_INDEX_PATH="$(mktemp -d)"
FILE="${KREW_INDEX_PATH}/plugins/score.yaml"
KUBE_SCORE_REPO_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." >/dev/null 2>&1 && pwd )"

checksum() {
    grep -E "kube-score_${VERSION}_${1}_amd64.tar.gz" ${KUBE_SCORE_REPO_ROOT}/dist/checksums.txt | awk '{print $1}'
}

gg() {
    git -C "$KREW_INDEX_PATH" "$@"
}

git clone git@github.com:kubernetes-sigs/krew-index.git "$KREW_INDEX_PATH"
git -C  "$KREW_INDEX_PATH" remote add zegl git@github.com:zegl/krew-index.git

gg checkout master
gg fetch origin
gg reset --hard origin/master
gg branch -D "kube-score-${VERSION}" || true
gg checkout -b "kube-score-${VERSION}"

yq --inplace ".spec.version = \"v${VERSION}\""  "${FILE}"

yq --inplace ".spec.platforms[0].uri = \"https://github.com/zegl/kube-score/releases/download/v${VERSION}/kube-score_${VERSION}_darwin_amd64.tar.gz\"" "$FILE"
yq --inplace ".spec.platforms[0].sha256 = \"$(checksum darwin)\"" "$FILE"

yq --inplace ".spec.platforms[1].uri = \"https://github.com/zegl/kube-score/releases/download/v${VERSION}/kube-score_${VERSION}_linux_amd64.tar.gz\"" "$FILE"
yq --inplace ".spec.platforms[1].sha256 = \"$(checksum linux)\"" "$FILE"

gg add plugins/score.yaml
gg commit -m "Update score to v${VERSION}"
gg push --force -u zegl "kube-score-${VERSION}"

open "https://github.com/kubernetes-sigs/krew-index/compare/master...zegl:kube-score-${VERSION}?expand=1"
07070100000023000081ED000000000000000000000001680FB1F800000382000000000000000000000000000000000000002900000000kube-score-1.20.0/hack/release-docker.sh#!/bin/bash

set -euo pipefail

function echoinfo() {
	LIGHT_GREEN='\033[1;32m'
	NC='\033[0m' # No Color
	printf "${LIGHT_GREEN}%s${NC}\n" "$1"
}


VERSION="$(git describe --tags --abbrev=0)"
TAG_ARGS="-t zegl/kube-score:${VERSION}"
PLATFORM_ARGS=""
PUSH_OR_LOAD_ARG="--load"

if [ -z ${PUSH_LATEST+x} ]; then
    echoinfo "[x] Dry run. (Set PUSH_LATEST if you want to push and tag latest)"
else 
    echoinfo "[x] Making production build. Will push to Docker Hub!"
    TAG_ARGS="${TAG_ARGS} -t zegl/kube-score:latest"
    PLATFORM_ARGS="--platform linux/arm64 --platform linux/amd64"
    PUSH_OR_LOAD_ARG="--push"
fi

docker buildx build \
    --build-arg KUBE_SCORE_VERSION=${VERSION} \
    --build-arg "KUBE_SCORE_COMMIT=$(git rev-parse HEAD)" \
    --build-arg "KUBE_SCORE_DATE=$(date -Iseconds)" \
    ${PLATFORM_ARGS} \
    ${TAG_ARGS} \
    ${PUSH_OR_LOAD_ARG} \
    --target runner \
    .
07070100000024000081ED000000000000000000000001680FB1F8000005DC000000000000000000000000000000000000002300000000kube-score-1.20.0/hack/relnotes.sh#!/usr/bin/env bash

set -euo pipefail

# Dependencies: rg (ripgrep), jq

if [ "$#" -ne 1 ]; then
    echo "Unexpected number of arguments"
    echo
    echo "Usage:"
    echo "  $0 previous-release-tag"
    exit 1;
fi

PREV_RELEASE=$1
CURRENT_TAG=$(git tag -l --points-at HEAD);

if [ "$(echo "$CURRENT_TAG" | wc -l)" -ne 1 ]; then
    echo "Could not detect version. HEAD should have exactly one tag.";
    exit 1;
fi

#
# Generate list of changes based on RELNOTES in commits
#
echo "# Changes";
RELNOTE_MERGES=$(git log ${PREV_RELEASE}...${CURRENT_TAG} --grep RELNOTE --oneline --merges)
while read -r line; do
    if [ -z "$line" ]; then
        continue;
    fi
    COMMIT=$(echo "$line" | awk '{print $1}')
    git show "$COMMIT" | rg -o '^\s+([0-9]+):(.*?)\s+RELNOTE:(.*?)\s+```' --multiline-dotall --multiline --replace "* #\$1 \$3" || true;
done <<< "$RELNOTE_MERGES"

#
# Authors secrion
#
echo
echo -n "This release contains contributions from: "
git log ${PREV_RELEASE}...HEAD | rg -o "Co-authored-by: (.*?) <" --replace "\$1" | sort |  uniq | grep -v "\[bot\]" | awk 'ORS=", "' | sed 's/, $//'

#
# Download instructions
#
echo
echo "# Download"
echo "* Download the binaries from the GitHub release page"
echo "* Download the image from Docker Hub: \`zegl/kube-score:${CURRENT_TAG}\` (Built for arm64 and amd64. Includes both Helm3 and Kustomize.)"
echo "* Download from homebrew: \`brew install kube-score/tap/kube-score\`"
echo "* Download with krew: \`kubectl krew install score\`"
07070100000025000081A4000000000000000000000001680FB1F8000000D0000000000000000000000000000000000000001F00000000kube-score-1.20.0/package.json{
    "name": "kube-score",
    "private": true,
    "version": "0.0.0",
    "type": "module",
    "packageManager": "pnpm@9.5.0+sha256.dbdf5961c32909fb030595a9daa1dae720162e658609a8f92f2fa99835510ca5"
  }
  07070100000026000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001900000000kube-score-1.20.0/parser07070100000027000081A4000000000000000000000001680FB1F80000015E000000000000000000000000000000000000002200000000kube-score-1.20.0/parser/error.gopackage parser

import (
	"strings"
)

type parseErrors []error

func (p parseErrors) Error() string {
	var s []string
	for _, e := range p {
		s = append(s, e.Error())
	}
	return strings.Join(s, "\n")
}

func (p *parseErrors) AddIfErr(err error) {
	if err != nil {
		*p = append(*p, err)
	}
}

func (p parseErrors) Any() bool {
	return len(p) > 0
}
07070100000028000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002200000000kube-score-1.20.0/parser/internal07070100000029000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002A00000000kube-score-1.20.0/parser/internal/cronjob0707010000002A000081A4000000000000000000000001680FB1F8000002F0000000000000000000000000000000000000003000000000kube-score-1.20.0/parser/internal/cronjob/v1.gopackage cronjob

import (
	ks "github.com/zegl/kube-score/domain"
	v1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type CronJobV1 struct {
	Obj      v1.CronJob
	Location ks.FileLocation
}

func (c CronJobV1) StartingDeadlineSeconds() *int64 {
	return c.Obj.Spec.StartingDeadlineSeconds
}

func (c CronJobV1) FileLocation() ks.FileLocation {
	return c.Location
}

func (c CronJobV1) GetTypeMeta() metav1.TypeMeta {
	return c.Obj.TypeMeta
}

func (c CronJobV1) GetObjectMeta() metav1.ObjectMeta {
	return c.Obj.ObjectMeta
}

func (c CronJobV1) GetPodTemplateSpec() corev1.PodTemplateSpec {
	t := c.Obj.Spec.JobTemplate.Spec.Template
	t.ObjectMeta.Namespace = c.Obj.ObjectMeta.Namespace
	return t
}
0707010000002B000081A4000000000000000000000001680FB1F800000315000000000000000000000000000000000000003500000000kube-score-1.20.0/parser/internal/cronjob/v1beta1.gopackage cronjob

import (
	ks "github.com/zegl/kube-score/domain"
	"k8s.io/api/batch/v1beta1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type CronJobV1beta1 struct {
	Obj      v1beta1.CronJob
	Location ks.FileLocation
}

func (c CronJobV1beta1) StartingDeadlineSeconds() *int64 {
	return c.Obj.Spec.StartingDeadlineSeconds
}

func (c CronJobV1beta1) FileLocation() ks.FileLocation {
	return c.Location
}

func (c CronJobV1beta1) GetTypeMeta() metav1.TypeMeta {
	return c.Obj.TypeMeta
}

func (c CronJobV1beta1) GetObjectMeta() metav1.ObjectMeta {
	return c.Obj.ObjectMeta
}

func (c CronJobV1beta1) GetPodTemplateSpec() corev1.PodTemplateSpec {
	t := c.Obj.Spec.JobTemplate.Spec.Template
	t.ObjectMeta.Namespace = c.Obj.ObjectMeta.Namespace
	return t
}
0707010000002C000081A4000000000000000000000001680FB1F80000070F000000000000000000000000000000000000002F00000000kube-score-1.20.0/parser/internal/deamonset.gopackage internal

import (
	appsv1 "k8s.io/api/apps/v1"
	appsv1beta2 "k8s.io/api/apps/v1beta2"
	corev1 "k8s.io/api/core/v1"
	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	ks "github.com/zegl/kube-score/domain"
)

type Appsv1DaemonSet struct {
	appsv1.DaemonSet
	Location ks.FileLocation
}

func (d Appsv1DaemonSet) FileLocation() ks.FileLocation {
	return d.Location
}

func (d Appsv1DaemonSet) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d Appsv1DaemonSet) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d Appsv1DaemonSet) GetPodTemplateSpec() corev1.PodTemplateSpec {
	d.Spec.Template.ObjectMeta.Namespace = d.ObjectMeta.Namespace
	return d.Spec.Template
}

type Appsv1beta2DaemonSet struct {
	appsv1beta2.DaemonSet
	Location ks.FileLocation
}

func (d Appsv1beta2DaemonSet) FileLocation() ks.FileLocation {
	return d.Location
}

func (d Appsv1beta2DaemonSet) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d Appsv1beta2DaemonSet) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d Appsv1beta2DaemonSet) GetPodTemplateSpec() corev1.PodTemplateSpec {
	d.Spec.Template.ObjectMeta.Namespace = d.ObjectMeta.Namespace
	return d.Spec.Template
}

type Extensionsv1beta1DaemonSet struct {
	extensionsv1beta1.DaemonSet
	Location ks.FileLocation
}

func (d Extensionsv1beta1DaemonSet) FileLocation() ks.FileLocation {
	return d.Location
}

func (d Extensionsv1beta1DaemonSet) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d Extensionsv1beta1DaemonSet) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d Extensionsv1beta1DaemonSet) GetPodTemplateSpec() corev1.PodTemplateSpec {
	d.Spec.Template.ObjectMeta.Namespace = d.ObjectMeta.Namespace
	return d.Spec.Template
}
0707010000002D000081A4000000000000000000000001680FB1F8000009B7000000000000000000000000000000000000003000000000kube-score-1.20.0/parser/internal/deployment.gopackage internal

import (
	appsv1 "k8s.io/api/apps/v1"
	appsv1beta1 "k8s.io/api/apps/v1beta1"
	appsv1beta2 "k8s.io/api/apps/v1beta2"
	corev1 "k8s.io/api/core/v1"
	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	ks "github.com/zegl/kube-score/domain"
)

type Appsv1Deployment struct {
	Obj      appsv1.Deployment
	Location ks.FileLocation
}

func (d Appsv1Deployment) FileLocation() ks.FileLocation {
	return d.Location
}

func (d Appsv1Deployment) GetTypeMeta() metav1.TypeMeta {
	return d.Obj.TypeMeta
}

func (d Appsv1Deployment) GetObjectMeta() metav1.ObjectMeta {
	return d.Obj.ObjectMeta
}

func (d Appsv1Deployment) GetPodTemplateSpec() corev1.PodTemplateSpec {
	d.Obj.Spec.Template.ObjectMeta.Namespace = d.Obj.ObjectMeta.Namespace
	return d.Obj.Spec.Template
}

func (d Appsv1Deployment) Deployment() appsv1.Deployment {
	return d.Obj
}

type Appsv1beta1Deployment struct {
	appsv1beta1.Deployment
	Location ks.FileLocation
}

func (d Appsv1beta1Deployment) FileLocation() ks.FileLocation {
	return d.Location
}

func (d Appsv1beta1Deployment) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d Appsv1beta1Deployment) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d Appsv1beta1Deployment) GetPodTemplateSpec() corev1.PodTemplateSpec {
	d.Spec.Template.ObjectMeta.Namespace = d.ObjectMeta.Namespace
	return d.Spec.Template
}

type Appsv1beta2Deployment struct {
	appsv1beta2.Deployment
	Location ks.FileLocation
}

func (d Appsv1beta2Deployment) FileLocation() ks.FileLocation {
	return d.Location
}

func (d Appsv1beta2Deployment) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d Appsv1beta2Deployment) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d Appsv1beta2Deployment) GetPodTemplateSpec() corev1.PodTemplateSpec {
	d.Spec.Template.ObjectMeta.Namespace = d.ObjectMeta.Namespace
	return d.Spec.Template
}

type Extensionsv1beta1Deployment struct {
	extensionsv1beta1.Deployment
	Location ks.FileLocation
}

func (d Extensionsv1beta1Deployment) FileLocation() ks.FileLocation {
	return d.Location
}

func (d Extensionsv1beta1Deployment) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d Extensionsv1beta1Deployment) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d Extensionsv1beta1Deployment) GetPodTemplateSpec() corev1.PodTemplateSpec {
	d.Spec.Template.ObjectMeta.Namespace = d.ObjectMeta.Namespace
	return d.Spec.Template
}
0707010000002E000081A4000000000000000000000001680FB1F800000963000000000000000000000000000000000000002900000000kube-score-1.20.0/parser/internal/hpa.gopackage internal

import (
	autoscalingv1 "k8s.io/api/autoscaling/v1"
	autoscalingv2 "k8s.io/api/autoscaling/v2"
	autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1"
	autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	ks "github.com/zegl/kube-score/domain"
)

type HPAv1 struct {
	autoscalingv1.HorizontalPodAutoscaler
	Location ks.FileLocation
}

func (d HPAv1) FileLocation() ks.FileLocation {
	return d.Location
}

func (d HPAv1) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d HPAv1) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}
func (d HPAv1) MinReplicas() *int32 {
	return d.Spec.MinReplicas
}

func (d HPAv1) HpaTarget() autoscalingv1.CrossVersionObjectReference {
	return d.Spec.ScaleTargetRef
}

type HPAv2beta1 struct {
	autoscalingv2beta1.HorizontalPodAutoscaler
	Location ks.FileLocation
}

func (d HPAv2beta1) FileLocation() ks.FileLocation {
	return d.Location
}

func (d HPAv2beta1) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d HPAv2beta1) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d HPAv2beta1) MinReplicas() *int32 {
	return d.Spec.MinReplicas
}

func (d HPAv2beta1) HpaTarget() autoscalingv1.CrossVersionObjectReference {
	return autoscalingv1.CrossVersionObjectReference(d.Spec.ScaleTargetRef)
}

type HPAv2beta2 struct {
	autoscalingv2beta2.HorizontalPodAutoscaler
	Location ks.FileLocation
}

func (d HPAv2beta2) FileLocation() ks.FileLocation {
	return d.Location
}

func (d HPAv2beta2) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d HPAv2beta2) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d HPAv2beta2) MinReplicas() *int32 {
	return d.Spec.MinReplicas
}

func (d HPAv2beta2) HpaTarget() autoscalingv1.CrossVersionObjectReference {
	return autoscalingv1.CrossVersionObjectReference(d.Spec.ScaleTargetRef)
}

type HPAv2 struct {
	autoscalingv2.HorizontalPodAutoscaler
	Location ks.FileLocation
}

func (d HPAv2) FileLocation() ks.FileLocation {
	return d.Location
}

func (d HPAv2) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d HPAv2) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d HPAv2) MinReplicas() *int32 {
	return d.Spec.MinReplicas
}

func (d HPAv2) HpaTarget() autoscalingv1.CrossVersionObjectReference {
	return autoscalingv1.CrossVersionObjectReference(d.Spec.ScaleTargetRef)
}
0707010000002F000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002A00000000kube-score-1.20.0/parser/internal/ingress07070100000030000081A4000000000000000000000001680FB1F800000D41000000000000000000000000000000000000002D00000000kube-score-1.20.0/parser/internal/ingress.gopackage internal

import (
	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
	networkingv1 "k8s.io/api/networking/v1"
	networkingv1beta1 "k8s.io/api/networking/v1beta1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	ks "github.com/zegl/kube-score/domain"
)

var _ ks.Ingress = (*IngressV1)(nil)
var _ ks.Ingress = (*IngressV1beta1)(nil)
var _ ks.Ingress = (*ExtensionsIngressV1beta1)(nil)

type IngressV1 struct {
	networkingv1.Ingress
	Location ks.FileLocation
}

func (i IngressV1) FileLocation() ks.FileLocation {
	return i.Location
}

func (i IngressV1) GetObjectMeta() v1.ObjectMeta {
	return i.ObjectMeta
}

func (i IngressV1) GetTypeMeta() v1.TypeMeta {
	return i.TypeMeta
}

func (i IngressV1) Rules() []networkingv1.IngressRule {
	return i.Spec.Rules
}

type IngressV1beta1 struct {
	networkingv1beta1.Ingress
	Location ks.FileLocation
}

func (i IngressV1beta1) FileLocation() ks.FileLocation {
	return i.Location
}

func (i IngressV1beta1) GetObjectMeta() v1.ObjectMeta {
	return i.ObjectMeta
}

func (i IngressV1beta1) GetTypeMeta() v1.TypeMeta {
	return i.TypeMeta
}

func (i IngressV1beta1) Rules() []networkingv1.IngressRule {
	var res []networkingv1.IngressRule

	paths := func(in []networkingv1beta1.HTTPIngressPath) (out []networkingv1.HTTPIngressPath) {
		for _, path := range in {
			out = append(out, networkingv1.HTTPIngressPath{
				Path: path.Path,
				Backend: networkingv1.IngressBackend{
					Service: &networkingv1.IngressServiceBackend{
						Name: path.Backend.ServiceName,
						Port: networkingv1.ServiceBackendPort{
							Name:   path.Backend.ServicePort.StrVal,
							Number: path.Backend.ServicePort.IntVal,
						},
					},
				},
			})
		}
		return
	}

	for _, rule := range i.Spec.Rules {
		var http *networkingv1.HTTPIngressRuleValue
		if rule.HTTP != nil {
			http = &networkingv1.HTTPIngressRuleValue{
				Paths: paths(rule.HTTP.Paths),
			}
		}

		res = append(res, networkingv1.IngressRule{
			Host: rule.Host,
			IngressRuleValue: networkingv1.IngressRuleValue{
				HTTP: http,
			},
		})
	}

	return res
}

type ExtensionsIngressV1beta1 struct {
	extensionsv1beta1.Ingress
	Location ks.FileLocation
}

func (i ExtensionsIngressV1beta1) GetObjectMeta() v1.ObjectMeta {
	return i.ObjectMeta
}

func (i ExtensionsIngressV1beta1) GetTypeMeta() v1.TypeMeta {
	return i.TypeMeta
}

func (i ExtensionsIngressV1beta1) Rules() []networkingv1.IngressRule {
	var res []networkingv1.IngressRule

	paths := func(in []extensionsv1beta1.HTTPIngressPath) (out []networkingv1.HTTPIngressPath) {
		for _, path := range in {
			out = append(out, networkingv1.HTTPIngressPath{
				Path: path.Path,
				Backend: networkingv1.IngressBackend{
					Service: &networkingv1.IngressServiceBackend{
						Name: path.Backend.ServiceName,
						Port: networkingv1.ServiceBackendPort{
							Name:   path.Backend.ServicePort.StrVal,
							Number: path.Backend.ServicePort.IntVal,
						},
					},
				},
			})
		}
		return
	}

	for _, rule := range i.Spec.Rules {
		var http *networkingv1.HTTPIngressRuleValue
		if rule.HTTP != nil {
			http = &networkingv1.HTTPIngressRuleValue{
				Paths: paths(rule.HTTP.Paths),
			}
		}

		res = append(res, networkingv1.IngressRule{
			Host: rule.Host,
			IngressRuleValue: networkingv1.IngressRuleValue{
				HTTP: http,
			},
		})
	}

	return res
}

func (i ExtensionsIngressV1beta1) FileLocation() ks.FileLocation {
	return i.Location
}
07070100000031000081A4000000000000000000000001680FB1F800000139000000000000000000000000000000000000003500000000kube-score-1.20.0/parser/internal/ingress/ingress.gopackage ingress

import (
	"k8s.io/api/extensions/v1beta1"

	ks "github.com/zegl/kube-score/domain"
)

type Ingress struct {
	Obj      v1beta1.Ingress
	Location ks.FileLocation
}

func (i Ingress) Ingress() v1beta1.Ingress {
	return i.Obj
}

func (i Ingress) FileLocation() ks.FileLocation {
	return i.Location
}
07070100000032000081A4000000000000000000000001680FB1F800000275000000000000000000000000000000000000002A00000000kube-score-1.20.0/parser/internal/jobs.gopackage internal

import (
	batchv1 "k8s.io/api/batch/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	ks "github.com/zegl/kube-score/domain"
)

type Batchv1Job struct {
	batchv1.Job
	Location ks.FileLocation
}

func (d Batchv1Job) FileLocation() ks.FileLocation {
	return d.Location
}

func (d Batchv1Job) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d Batchv1Job) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d Batchv1Job) GetPodTemplateSpec() corev1.PodTemplateSpec {
	d.Spec.Template.ObjectMeta.Namespace = d.ObjectMeta.Namespace
	return d.Spec.Template
}
07070100000033000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000003000000000kube-score-1.20.0/parser/internal/networkpolicy07070100000034000081A4000000000000000000000001680FB1F80000016B000000000000000000000000000000000000003700000000kube-score-1.20.0/parser/internal/networkpolicy/pod.gopackage pod

import (
	networkingv1 "k8s.io/api/networking/v1"

	ks "github.com/zegl/kube-score/domain"
)

type NetworkPolicy struct {
	Obj      networkingv1.NetworkPolicy
	Location ks.FileLocation
}

func (p NetworkPolicy) NetworkPolicy() networkingv1.NetworkPolicy {
	return p.Obj
}

func (p NetworkPolicy) FileLocation() ks.FileLocation {
	return p.Location
}
07070100000035000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002600000000kube-score-1.20.0/parser/internal/pdb07070100000036000081A4000000000000000000000001680FB1F8000007CB000000000000000000000000000000000000002D00000000kube-score-1.20.0/parser/internal/pdb/pod.gopackage pod

import (
	policyv1 "k8s.io/api/policy/v1"
	policyv1beta1 "k8s.io/api/policy/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	ks "github.com/zegl/kube-score/domain"
)

type PodDisruptionBudgetV1beta1 struct {
	Obj      policyv1beta1.PodDisruptionBudget
	Location ks.FileLocation
}

func (p PodDisruptionBudgetV1beta1) GetObjectMeta() metav1.ObjectMeta {
	return p.Obj.ObjectMeta
}

func (p PodDisruptionBudgetV1beta1) GetTypeMeta() metav1.TypeMeta {
	return p.Obj.TypeMeta
}

func (p PodDisruptionBudgetV1beta1) PodDisruptionBudgetSelector() *metav1.LabelSelector {
	return p.Obj.Spec.Selector
}

func (p PodDisruptionBudgetV1beta1) Namespace() string {
	return p.Obj.Namespace
}

func (p PodDisruptionBudgetV1beta1) FileLocation() ks.FileLocation {
	return p.Location
}

func (p PodDisruptionBudgetV1beta1) Spec() policyv1.PodDisruptionBudgetSpec {
	var polType *policyv1.UnhealthyPodEvictionPolicyType
	if p.Obj.Spec.UnhealthyPodEvictionPolicy != nil {
		t := policyv1.UnhealthyPodEvictionPolicyType(*p.Obj.Spec.UnhealthyPodEvictionPolicy)
		polType = &t
	}

	return policyv1.PodDisruptionBudgetSpec{
		MinAvailable:               p.Obj.Spec.MinAvailable,
		Selector:                   p.Obj.Spec.Selector,
		MaxUnavailable:             p.Obj.Spec.MaxUnavailable,
		UnhealthyPodEvictionPolicy: polType,
	}
}

type PodDisruptionBudgetV1 struct {
	Obj      policyv1.PodDisruptionBudget
	Location ks.FileLocation
}

func (p PodDisruptionBudgetV1) GetObjectMeta() metav1.ObjectMeta {
	return p.Obj.ObjectMeta
}

func (p PodDisruptionBudgetV1) GetTypeMeta() metav1.TypeMeta {
	return p.Obj.TypeMeta
}

func (p PodDisruptionBudgetV1) PodDisruptionBudgetSelector() *metav1.LabelSelector {
	return p.Obj.Spec.Selector
}

func (p PodDisruptionBudgetV1) FileLocation() ks.FileLocation {
	return p.Location
}

func (p PodDisruptionBudgetV1) Namespace() string {
	return p.Obj.Namespace
}

func (p PodDisruptionBudgetV1) Spec() policyv1.PodDisruptionBudgetSpec {
	return p.Obj.Spec
}
07070100000037000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002600000000kube-score-1.20.0/parser/internal/pod07070100000038000081A4000000000000000000000001680FB1F800000117000000000000000000000000000000000000002D00000000kube-score-1.20.0/parser/internal/pod/pod.gopackage pod

import (
	corev1 "k8s.io/api/core/v1"

	ks "github.com/zegl/kube-score/domain"
)

type Pod struct {
	Obj      corev1.Pod
	Location ks.FileLocation
}

func (p Pod) Pod() corev1.Pod {
	return p.Obj
}

func (p Pod) FileLocation() ks.FileLocation {
	return p.Location
}
07070100000039000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002A00000000kube-score-1.20.0/parser/internal/service0707010000003A000081A4000000000000000000000001680FB1F800000123000000000000000000000000000000000000003100000000kube-score-1.20.0/parser/internal/service/pod.gopackage pod

import (
	v1 "k8s.io/api/core/v1"

	ks "github.com/zegl/kube-score/domain"
)

type Service struct {
	Obj      v1.Service
	Location ks.FileLocation
}

func (p Service) Service() v1.Service {
	return p.Obj
}

func (p Service) FileLocation() ks.FileLocation {
	return p.Location
}
0707010000003B000081A4000000000000000000000001680FB1F80000076F000000000000000000000000000000000000003100000000kube-score-1.20.0/parser/internal/statefulset.gopackage internal

import (
	appsv1 "k8s.io/api/apps/v1"
	appsv1beta1 "k8s.io/api/apps/v1beta1"
	appsv1beta2 "k8s.io/api/apps/v1beta2"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	ks "github.com/zegl/kube-score/domain"
)

type Appsv1StatefulSet struct {
	Obj      appsv1.StatefulSet
	Location ks.FileLocation
}

func (d Appsv1StatefulSet) FileLocation() ks.FileLocation {
	return d.Location
}

func (s Appsv1StatefulSet) GetTypeMeta() metav1.TypeMeta {
	return s.Obj.TypeMeta
}

func (s Appsv1StatefulSet) GetObjectMeta() metav1.ObjectMeta {
	return s.Obj.ObjectMeta
}

func (s Appsv1StatefulSet) GetPodTemplateSpec() corev1.PodTemplateSpec {
	s.Obj.Spec.Template.ObjectMeta.Namespace = s.Obj.ObjectMeta.Namespace
	return s.Obj.Spec.Template
}

func (s Appsv1StatefulSet) StatefulSet() appsv1.StatefulSet {
	return s.Obj
}

type Appsv1beta1StatefulSet struct {
	appsv1beta1.StatefulSet
	Location ks.FileLocation
}

func (d Appsv1beta1StatefulSet) FileLocation() ks.FileLocation {
	return d.Location
}

func (s Appsv1beta1StatefulSet) GetTypeMeta() metav1.TypeMeta {
	return s.TypeMeta
}

func (s Appsv1beta1StatefulSet) GetObjectMeta() metav1.ObjectMeta {
	return s.ObjectMeta
}

func (s Appsv1beta1StatefulSet) GetPodTemplateSpec() corev1.PodTemplateSpec {
	s.Spec.Template.ObjectMeta.Namespace = s.ObjectMeta.Namespace
	return s.Spec.Template
}

type Appsv1beta2StatefulSet struct {
	appsv1beta2.StatefulSet
	Location ks.FileLocation
}

func (d Appsv1beta2StatefulSet) FileLocation() ks.FileLocation {
	return d.Location
}

func (s Appsv1beta2StatefulSet) GetTypeMeta() metav1.TypeMeta {
	return s.TypeMeta
}

func (s Appsv1beta2StatefulSet) GetObjectMeta() metav1.ObjectMeta {
	return s.ObjectMeta
}

func (s Appsv1beta2StatefulSet) GetPodTemplateSpec() corev1.PodTemplateSpec {
	s.Spec.Template.ObjectMeta.Namespace = s.ObjectMeta.Namespace
	return s.Spec.Template
}
0707010000003C000081A4000000000000000000000001680FB1F800003C71000000000000000000000000000000000000002200000000kube-score-1.20.0/parser/parse.gopackage parser

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"strings"

	"gopkg.in/yaml.v3"
	appsv1 "k8s.io/api/apps/v1"
	appsv1beta1 "k8s.io/api/apps/v1beta1"
	appsv1beta2 "k8s.io/api/apps/v1beta2"
	autoscalingv1 "k8s.io/api/autoscaling/v1"
	autoscalingv2 "k8s.io/api/autoscaling/v2"
	autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1"
	autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
	batchv1 "k8s.io/api/batch/v1"
	batchv1beta1 "k8s.io/api/batch/v1beta1"
	corev1 "k8s.io/api/core/v1"
	extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
	networkingv1 "k8s.io/api/networking/v1"
	networkingv1beta1 "k8s.io/api/networking/v1beta1"
	policyv1 "k8s.io/api/policy/v1"
	policyv1beta1 "k8s.io/api/policy/v1beta1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer"

	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/parser/internal"
	internalcronjob "github.com/zegl/kube-score/parser/internal/cronjob"
	internalnetpol "github.com/zegl/kube-score/parser/internal/networkpolicy"
	internalpdb "github.com/zegl/kube-score/parser/internal/pdb"
	internalpod "github.com/zegl/kube-score/parser/internal/pod"
	internalservice "github.com/zegl/kube-score/parser/internal/service"
)

type Parser struct {
	scheme *runtime.Scheme
	codecs serializer.CodecFactory
	config *Config
}

type Config struct {
	VerboseOutput int
}

type schemaAdderFunc func(scheme *runtime.Scheme) error

func New(config *Config) (*Parser, error) {
	if config == nil {
		config = &Config{}
	}

	scheme := runtime.NewScheme()
	p := &Parser{
		scheme: scheme,
		codecs: serializer.NewCodecFactory(scheme),
		config: config,
	}
	if err := p.addToScheme(); err != nil {
		return nil, fmt.Errorf("failed to init: %w", err)
	}
	return p, nil
}

func (p *Parser) addToScheme() error {
	var adders = []schemaAdderFunc{
		corev1.AddToScheme,
		appsv1.AddToScheme,
		networkingv1.AddToScheme,
		extensionsv1beta1.AddToScheme,
		appsv1beta1.AddToScheme,
		appsv1beta2.AddToScheme,
		batchv1.AddToScheme,
		batchv1beta1.AddToScheme,
		policyv1beta1.AddToScheme,
		policyv1.AddToScheme,
	}

	for _, adder := range adders {
		if err := adder(p.scheme); err != nil {
			return fmt.Errorf("failed to add type to schema: %w", err)
		}
	}

	return nil
}

type detectKind struct {
	ApiVersion string `yaml:"apiVersion"`
	Kind       string `yaml:"kind"`
}

type parsedObjects struct {
	bothMetas            []ks.BothMeta
	pods                 []ks.Pod
	podspecers           []ks.PodSpecer
	networkPolicies      []ks.NetworkPolicy
	services             []ks.Service
	podDisruptionBudgets []ks.PodDisruptionBudget
	deployments          []ks.Deployment
	statefulsets         []ks.StatefulSet
	ingresses            []ks.Ingress // supports multiple versions of ingress
	cronjobs             []ks.CronJob
	hpaTargeters         []ks.HpaTargeter // all versions of HPAs
}

func (p *parsedObjects) Services() []ks.Service {
	return p.services
}

func (p *parsedObjects) Pods() []ks.Pod {
	return p.pods
}

func (p *parsedObjects) PodSpeccers() []ks.PodSpecer {
	return p.podspecers
}

func (p *parsedObjects) Ingresses() []ks.Ingress {
	return p.ingresses
}

func (p *parsedObjects) PodDisruptionBudgets() []ks.PodDisruptionBudget {
	return p.podDisruptionBudgets
}

func (p *parsedObjects) CronJobs() []ks.CronJob {
	return p.cronjobs
}

func (p *parsedObjects) Deployments() []ks.Deployment {
	return p.deployments
}

func (p *parsedObjects) StatefulSets() []ks.StatefulSet {
	return p.statefulsets
}

func (p *parsedObjects) Metas() []ks.BothMeta {
	return p.bothMetas
}

func (p *parsedObjects) NetworkPolicies() []ks.NetworkPolicy {
	return p.networkPolicies
}

func (p *parsedObjects) HorizontalPodAutoscalers() []ks.HpaTargeter {
	return p.hpaTargeters
}

func Empty() ks.AllTypes {
	return &parsedObjects{}
}

func (p *Parser) ParseFiles(files []ks.NamedReader) (ks.AllTypes, error) {
	s := &parsedObjects{}

	for _, namedReader := range files {
		fullFile, err := io.ReadAll(namedReader)
		if err != nil {
			return nil, err
		}

		// Convert to unix style newlines
		fullFile = bytes.ReplaceAll(fullFile, []byte("\r\n"), []byte("\n"))

		offset := 1 // Line numbers are 1 indexed

		// Remove initial "---\n" if present
		if bytes.HasPrefix(fullFile, []byte("---\n")) {
			fullFile = fullFile[4:]
			offset = 2
		}

		for _, fileContents := range bytes.Split(fullFile, []byte("\n---\n")) {

			if len(bytes.TrimSpace(fileContents)) > 0 {
				if err := p.detectAndDecode(s, namedReader.Name(), offset, fileContents); err != nil {
					return nil, err
				}
			}

			offset += 2 + bytes.Count(fileContents, []byte("\n"))
		}
	}

	return s, nil
}

func (p *Parser) detectAndDecode(s *parsedObjects, fileName string, fileOffset int, raw []byte) error {
	var detect detectKind
	err := yaml.Unmarshal(raw, &detect)
	if err != nil {
		return err
	}

	detectedVersion := schema.FromAPIVersionAndKind(detect.ApiVersion, detect.Kind)

	// Parse lists and their items recursively
	if detectedVersion == corev1.SchemeGroupVersion.WithKind("List") {
		var list corev1.List
		err := p.decode(raw, &list)
		if err != nil {
			return err
		}
		for _, listItem := range list.Items {
			err := p.detectAndDecode(s, fileName, fileOffset, listItem.Raw)
			if err != nil {
				return err
			}
		}
		return nil
	}

	err = p.decodeItem(s, detectedVersion, fileName, fileOffset, raw)
	if err != nil {
		return err
	}

	return nil
}

func (p *Parser) decode(data []byte, object runtime.Object) error {
	deserializer := p.codecs.UniversalDeserializer()
	if _, _, err := deserializer.Decode(data, nil, object); err != nil {
		gvk := object.GetObjectKind().GroupVersionKind()
		return fmt.Errorf("Failed to parse %s: err=%w", gvk, err)
	}
	return nil
}

func detectFileLocation(fileName string, fileOffset int, fileContents []byte) ks.FileLocation {
	// If the object YAML begins with a Helm style "# Source: " comment
	// Use the information in there as the file name
	firstRow := string(bytes.Split(fileContents, []byte("\n"))[0])
	helmTemplatePrefix := "# Source: "
	if strings.HasPrefix(firstRow, helmTemplatePrefix) {
		return ks.FileLocation{
			Name: firstRow[len(helmTemplatePrefix):],
			Line: 1, // Set line to 1 as the line definition gets lost in Helm
		}
	}

	return ks.FileLocation{
		Name: fileName,
		Line: fileOffset,
	}
}

func (p *Parser) decodeItem(s *parsedObjects, detectedVersion schema.GroupVersionKind, fileName string, fileOffset int, fileContents []byte) error {
	addPodSpeccer := func(ps ks.PodSpecer) {
		s.podspecers = append(s.podspecers, ps)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{
			TypeMeta:       ps.GetTypeMeta(),
			ObjectMeta:     ps.GetObjectMeta(),
			FileLocationer: ps,
		})
	}

	fileLocation := detectFileLocation(fileName, fileOffset, fileContents)

	var errs parseErrors

	switch detectedVersion {
	case corev1.SchemeGroupVersion.WithKind("Pod"):
		var pod corev1.Pod
		errs.AddIfErr(p.decode(fileContents, &pod))
		p := internalpod.Pod{Obj: pod, Location: fileLocation}
		s.pods = append(s.pods, p)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: pod.TypeMeta, ObjectMeta: pod.ObjectMeta, FileLocationer: p})

	case batchv1.SchemeGroupVersion.WithKind("Job"):
		var job batchv1.Job
		errs.AddIfErr(p.decode(fileContents, &job))
		addPodSpeccer(internal.Batchv1Job{Job: job, Location: fileLocation})

	case batchv1beta1.SchemeGroupVersion.WithKind("CronJob"):
		var cronjob batchv1beta1.CronJob
		errs.AddIfErr(p.decode(fileContents, &cronjob))
		cjob := internalcronjob.CronJobV1beta1{Obj: cronjob, Location: fileLocation}
		addPodSpeccer(cjob)
		s.cronjobs = append(s.cronjobs, cjob)

	case batchv1.SchemeGroupVersion.WithKind("CronJob"):
		var cronjob batchv1.CronJob
		errs.AddIfErr(p.decode(fileContents, &cronjob))
		cjob := internalcronjob.CronJobV1{Obj: cronjob, Location: fileLocation}
		addPodSpeccer(cjob)
		s.cronjobs = append(s.cronjobs, cjob)

	case appsv1.SchemeGroupVersion.WithKind("Deployment"):
		var deployment appsv1.Deployment
		errs.AddIfErr(p.decode(fileContents, &deployment))
		deploy := internal.Appsv1Deployment{Obj: deployment, Location: fileLocation}
		addPodSpeccer(deploy)

		// TODO: Support older versions of Deployment as well?
		s.deployments = append(s.deployments, deploy)
	case appsv1beta1.SchemeGroupVersion.WithKind("Deployment"):
		var deployment appsv1beta1.Deployment
		errs.AddIfErr(p.decode(fileContents, &deployment))
		addPodSpeccer(internal.Appsv1beta1Deployment{Deployment: deployment, Location: fileLocation})
	case appsv1beta2.SchemeGroupVersion.WithKind("Deployment"):
		var deployment appsv1beta2.Deployment
		errs.AddIfErr(p.decode(fileContents, &deployment))
		addPodSpeccer(internal.Appsv1beta2Deployment{Deployment: deployment, Location: fileLocation})
	case extensionsv1beta1.SchemeGroupVersion.WithKind("Deployment"):
		var deployment extensionsv1beta1.Deployment
		errs.AddIfErr(p.decode(fileContents, &deployment))
		addPodSpeccer(internal.Extensionsv1beta1Deployment{Deployment: deployment, Location: fileLocation})

	case appsv1.SchemeGroupVersion.WithKind("StatefulSet"):
		var statefulSet appsv1.StatefulSet
		errs.AddIfErr(p.decode(fileContents, &statefulSet))
		sset := internal.Appsv1StatefulSet{Obj: statefulSet, Location: fileLocation}
		addPodSpeccer(sset)

		// TODO: Support older versions of StatefulSet as well?
		s.statefulsets = append(s.statefulsets, sset)
	case appsv1beta1.SchemeGroupVersion.WithKind("StatefulSet"):
		var statefulSet appsv1beta1.StatefulSet
		errs.AddIfErr(p.decode(fileContents, &statefulSet))
		addPodSpeccer(internal.Appsv1beta1StatefulSet{StatefulSet: statefulSet, Location: fileLocation})
	case appsv1beta2.SchemeGroupVersion.WithKind("StatefulSet"):
		var statefulSet appsv1beta2.StatefulSet
		errs.AddIfErr(p.decode(fileContents, &statefulSet))
		addPodSpeccer(internal.Appsv1beta2StatefulSet{StatefulSet: statefulSet, Location: fileLocation})

	case appsv1.SchemeGroupVersion.WithKind("DaemonSet"):
		var daemonset appsv1.DaemonSet
		errs.AddIfErr(p.decode(fileContents, &daemonset))
		addPodSpeccer(internal.Appsv1DaemonSet{DaemonSet: daemonset, Location: fileLocation})
	case appsv1beta2.SchemeGroupVersion.WithKind("DaemonSet"):
		var daemonset appsv1beta2.DaemonSet
		errs.AddIfErr(p.decode(fileContents, &daemonset))
		addPodSpeccer(internal.Appsv1beta2DaemonSet{DaemonSet: daemonset, Location: fileLocation})
	case extensionsv1beta1.SchemeGroupVersion.WithKind("DaemonSet"):
		var daemonset extensionsv1beta1.DaemonSet
		errs.AddIfErr(p.decode(fileContents, &daemonset))
		addPodSpeccer(internal.Extensionsv1beta1DaemonSet{DaemonSet: daemonset, Location: fileLocation})

	case networkingv1.SchemeGroupVersion.WithKind("NetworkPolicy"):
		var netpol networkingv1.NetworkPolicy
		errs.AddIfErr(p.decode(fileContents, &netpol))
		np := internalnetpol.NetworkPolicy{Obj: netpol, Location: fileLocation}
		s.networkPolicies = append(s.networkPolicies, np)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: netpol.TypeMeta, ObjectMeta: netpol.ObjectMeta, FileLocationer: np})

	case corev1.SchemeGroupVersion.WithKind("Service"):
		var service corev1.Service
		errs.AddIfErr(p.decode(fileContents, &service))
		serv := internalservice.Service{Obj: service, Location: fileLocation}
		s.services = append(s.services, serv)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: service.TypeMeta, ObjectMeta: service.ObjectMeta, FileLocationer: serv})

	case policyv1beta1.SchemeGroupVersion.WithKind("PodDisruptionBudget"):
		var disruptBudget policyv1beta1.PodDisruptionBudget
		errs.AddIfErr(p.decode(fileContents, &disruptBudget))
		dbug := internalpdb.PodDisruptionBudgetV1beta1{Obj: disruptBudget, Location: fileLocation}
		s.podDisruptionBudgets = append(s.podDisruptionBudgets, dbug)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: disruptBudget.TypeMeta, ObjectMeta: disruptBudget.ObjectMeta, FileLocationer: dbug})
	case policyv1.SchemeGroupVersion.WithKind("PodDisruptionBudget"):
		var disruptBudget policyv1.PodDisruptionBudget
		errs.AddIfErr(p.decode(fileContents, &disruptBudget))
		dbug := internalpdb.PodDisruptionBudgetV1{Obj: disruptBudget, Location: fileLocation}
		s.podDisruptionBudgets = append(s.podDisruptionBudgets, dbug)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{
			TypeMeta:       disruptBudget.TypeMeta,
			ObjectMeta:     disruptBudget.ObjectMeta,
			FileLocationer: dbug,
		})

	case extensionsv1beta1.SchemeGroupVersion.WithKind("Ingress"):
		var ingress extensionsv1beta1.Ingress
		errs.AddIfErr(p.decode(fileContents, &ingress))
		ing := internal.ExtensionsIngressV1beta1{Ingress: ingress, Location: fileLocation}
		s.ingresses = append(s.ingresses, ing)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: ingress.TypeMeta, ObjectMeta: ingress.ObjectMeta, FileLocationer: ing})

	case networkingv1beta1.SchemeGroupVersion.WithKind("Ingress"):
		var ingress networkingv1beta1.Ingress
		errs.AddIfErr(p.decode(fileContents, &ingress))
		ing := internal.IngressV1beta1{Ingress: ingress, Location: fileLocation}
		s.ingresses = append(s.ingresses, ing)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: ingress.TypeMeta, ObjectMeta: ingress.ObjectMeta, FileLocationer: ing})

	case networkingv1.SchemeGroupVersion.WithKind("Ingress"):
		var ingress networkingv1.Ingress
		errs.AddIfErr(p.decode(fileContents, &ingress))
		ing := internal.IngressV1{Ingress: ingress, Location: fileLocation}
		s.ingresses = append(s.ingresses, ing)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: ingress.TypeMeta, ObjectMeta: ingress.ObjectMeta, FileLocationer: ing})

	case autoscalingv1.SchemeGroupVersion.WithKind("HorizontalPodAutoscaler"):
		var hpa autoscalingv1.HorizontalPodAutoscaler
		errs.AddIfErr(p.decode(fileContents, &hpa))
		h := internal.HPAv1{HorizontalPodAutoscaler: hpa, Location: fileLocation}
		s.hpaTargeters = append(s.hpaTargeters, h)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: hpa.TypeMeta, ObjectMeta: hpa.ObjectMeta, FileLocationer: h})

	case autoscalingv2beta1.SchemeGroupVersion.WithKind("HorizontalPodAutoscaler"):
		var hpa autoscalingv2beta1.HorizontalPodAutoscaler
		errs.AddIfErr(p.decode(fileContents, &hpa))
		h := internal.HPAv2beta1{HorizontalPodAutoscaler: hpa, Location: fileLocation}
		s.hpaTargeters = append(s.hpaTargeters, h)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: hpa.TypeMeta, ObjectMeta: hpa.ObjectMeta, FileLocationer: h})

	case autoscalingv2beta2.SchemeGroupVersion.WithKind("HorizontalPodAutoscaler"):
		var hpa autoscalingv2beta2.HorizontalPodAutoscaler
		errs.AddIfErr(p.decode(fileContents, &hpa))
		h := internal.HPAv2beta2{HorizontalPodAutoscaler: hpa, Location: fileLocation}
		s.hpaTargeters = append(s.hpaTargeters, h)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{
			TypeMeta:       hpa.TypeMeta,
			ObjectMeta:     hpa.ObjectMeta,
			FileLocationer: h,
		})

	case autoscalingv2.SchemeGroupVersion.WithKind("HorizontalPodAutoscaler"):
		var hpa autoscalingv2.HorizontalPodAutoscaler
		errs.AddIfErr(p.decode(fileContents, &hpa))
		h := internal.HPAv2{HorizontalPodAutoscaler: hpa, Location: fileLocation}
		s.hpaTargeters = append(s.hpaTargeters, h)
		s.bothMetas = append(s.bothMetas, ks.BothMeta{TypeMeta: hpa.TypeMeta, ObjectMeta: hpa.ObjectMeta, FileLocationer: h})

	default:
		if p.config.VerboseOutput > 1 {
			log.Printf("Unknown datatype: %s", detectedVersion.String())
		}
	}

	if errs.Any() {
		return errs
	}
	return nil
}
0707010000003D000081A4000000000000000000000001680FB1F8000005A0000000000000000000000000000000000000002700000000kube-score-1.20.0/parser/parse_test.gopackage parser

import (
	"fmt"
	"os"
	"testing"

	ks "github.com/zegl/kube-score/domain"

	"github.com/stretchr/testify/assert"
)

func TestParse(t *testing.T) {
	cases := []struct {
		fname    string
		expected error
	}{
		{
			"testdata/invalid-yaml.yaml",
			fmt.Errorf("Failed to parse /v1, Kind=Service: err=json: cannot unmarshal string into Go struct field ServicePort.spec.ports.nodePort of type int32"),
		}, {
			"testdata/valid-yaml.yaml",
			nil,
		},
	}

	parser, err := New(nil)
	assert.NoError(t, err)

	for _, tc := range cases {
		fp, err := os.Open(tc.fname)
		assert.Nil(t, err)
		_, err = parser.ParseFiles(
			[]ks.NamedReader{fp},
		)
		if tc.expected == nil {
			assert.Nil(t, err)
		} else {
			assert.Equal(t, tc.expected.Error(), err.Error())
		}
	}
}

func TestFileLocationHelm(t *testing.T) {
	doc := `# Source: app1/templates/deployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
  name: foo
spec:
  template:
    metadata:
      labels:
        foo: bar`

	fl := detectFileLocation("someName", 1, []byte(doc))
	assert.Equal(t, "app1/templates/deployment.yaml", fl.Name)
	assert.Equal(t, 1, fl.Line)
}

func TestFileLocation(t *testing.T) {
	doc := `kind: Deployment
apiVersion: apps/v1
metadata:
  name: foo
spec:
  template:
    metadata:
      labels:
        foo: bar`

	fl := detectFileLocation("someName", 123, []byte(doc))
	assert.Equal(t, "someName", fl.Name)
	assert.Equal(t, 123, fl.Line)
}
0707010000003E000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002200000000kube-score-1.20.0/parser/testdata0707010000003F000081A4000000000000000000000001680FB1F8000000DA000000000000000000000000000000000000003400000000kube-score-1.20.0/parser/testdata/invalid-yaml.yamlapiVersion: v1
kind: Service
metadata:
  name: ${APP}-service
  namespace: test
spec:
  type: NodePort
  ports:
  - port: ${PORT_EXPOSE}
    targetPort: ${PORT_EXPOSE}
    nodePort: ${PORT}
  selector:
    app: ${APP}
07070100000040000081A4000000000000000000000001680FB1F8000000BD000000000000000000000000000000000000003200000000kube-score-1.20.0/parser/testdata/valid-yaml.yamlapiVersion: v1
kind: Service
metadata:
  name: xyz-service
  namespace: test
spec:
  type: NodePort
  ports:
  - port: 123
    targetPort: 4444
    nodePort: 32444
  selector:
    app: xyz
07070100000041000081A4000000000000000000000001680FB1F80000E181000000000000000000000000000000000000002100000000kube-score-1.20.0/pnpm-lock.yamllockfileVersion: '9.0'

settings:
  autoInstallPeers: true
  excludeLinksFromLockfile: false

importers:

  .: {}

  web:
    dependencies:
      react:
        specifier: ^18.3.1
        version: 18.3.1
      react-dom:
        specifier: ^18.3.1
        version: 18.3.1(react@18.3.1)
      styled-components:
        specifier: ^6.1.11
        version: 6.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
    devDependencies:
      '@types/react':
        specifier: ^18.3.3
        version: 18.3.3
      '@types/react-dom':
        specifier: ^18.3.0
        version: 18.3.0
      '@typescript-eslint/eslint-plugin':
        specifier: ^8.8.1
        version: 8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.6.3))(eslint@9.12.0)(typescript@5.6.3)
      '@typescript-eslint/parser':
        specifier: ^8.8.1
        version: 8.8.1(eslint@9.12.0)(typescript@5.6.3)
      '@vitejs/plugin-react-swc':
        specifier: ^3.7.1
        version: 3.7.1(vite@5.4.8)
      eslint:
        specifier: ^9.12.0
        version: 9.12.0
      eslint-plugin-react-hooks:
        specifier: ^5.0.0
        version: 5.0.0(eslint@9.12.0)
      eslint-plugin-react-refresh:
        specifier: ^0.4.12
        version: 0.4.12(eslint@9.12.0)
      typescript:
        specifier: ^5.6.3
        version: 5.6.3
      vite:
        specifier: ^5.4.8
        version: 5.4.8

packages:

  '@emotion/is-prop-valid@1.2.2':
    resolution: {integrity: sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==}

  '@emotion/memoize@0.8.1':
    resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==}

  '@emotion/unitless@0.8.1':
    resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}

  '@esbuild/aix-ppc64@0.21.5':
    resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
    engines: {node: '>=12'}
    cpu: [ppc64]
    os: [aix]

  '@esbuild/android-arm64@0.21.5':
    resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [android]

  '@esbuild/android-arm@0.21.5':
    resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
    engines: {node: '>=12'}
    cpu: [arm]
    os: [android]

  '@esbuild/android-x64@0.21.5':
    resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [android]

  '@esbuild/darwin-arm64@0.21.5':
    resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [darwin]

  '@esbuild/darwin-x64@0.21.5':
    resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [darwin]

  '@esbuild/freebsd-arm64@0.21.5':
    resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [freebsd]

  '@esbuild/freebsd-x64@0.21.5':
    resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [freebsd]

  '@esbuild/linux-arm64@0.21.5':
    resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [linux]

  '@esbuild/linux-arm@0.21.5':
    resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
    engines: {node: '>=12'}
    cpu: [arm]
    os: [linux]

  '@esbuild/linux-ia32@0.21.5':
    resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
    engines: {node: '>=12'}
    cpu: [ia32]
    os: [linux]

  '@esbuild/linux-loong64@0.21.5':
    resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
    engines: {node: '>=12'}
    cpu: [loong64]
    os: [linux]

  '@esbuild/linux-mips64el@0.21.5':
    resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
    engines: {node: '>=12'}
    cpu: [mips64el]
    os: [linux]

  '@esbuild/linux-ppc64@0.21.5':
    resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
    engines: {node: '>=12'}
    cpu: [ppc64]
    os: [linux]

  '@esbuild/linux-riscv64@0.21.5':
    resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
    engines: {node: '>=12'}
    cpu: [riscv64]
    os: [linux]

  '@esbuild/linux-s390x@0.21.5':
    resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
    engines: {node: '>=12'}
    cpu: [s390x]
    os: [linux]

  '@esbuild/linux-x64@0.21.5':
    resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [linux]

  '@esbuild/netbsd-x64@0.21.5':
    resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [netbsd]

  '@esbuild/openbsd-x64@0.21.5':
    resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [openbsd]

  '@esbuild/sunos-x64@0.21.5':
    resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [sunos]

  '@esbuild/win32-arm64@0.21.5':
    resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [win32]

  '@esbuild/win32-ia32@0.21.5':
    resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
    engines: {node: '>=12'}
    cpu: [ia32]
    os: [win32]

  '@esbuild/win32-x64@0.21.5':
    resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [win32]

  '@eslint-community/eslint-utils@4.4.0':
    resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
    peerDependencies:
      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0

  '@eslint-community/regexpp@4.11.0':
    resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==}
    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}

  '@eslint/config-array@0.18.0':
    resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  '@eslint/core@0.6.0':
    resolution: {integrity: sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  '@eslint/eslintrc@3.1.0':
    resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  '@eslint/js@9.12.0':
    resolution: {integrity: sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  '@eslint/object-schema@2.1.4':
    resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  '@eslint/plugin-kit@0.2.0':
    resolution: {integrity: sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  '@humanfs/core@0.19.0':
    resolution: {integrity: sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==}
    engines: {node: '>=18.18.0'}

  '@humanfs/node@0.16.5':
    resolution: {integrity: sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==}
    engines: {node: '>=18.18.0'}

  '@humanwhocodes/module-importer@1.0.1':
    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
    engines: {node: '>=12.22'}

  '@humanwhocodes/retry@0.3.1':
    resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==}
    engines: {node: '>=18.18'}

  '@nodelib/fs.scandir@2.1.5':
    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
    engines: {node: '>= 8'}

  '@nodelib/fs.stat@2.0.5':
    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
    engines: {node: '>= 8'}

  '@nodelib/fs.walk@1.2.8':
    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
    engines: {node: '>= 8'}

  '@rollup/rollup-android-arm-eabi@4.24.0':
    resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==}
    cpu: [arm]
    os: [android]

  '@rollup/rollup-android-arm64@4.24.0':
    resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==}
    cpu: [arm64]
    os: [android]

  '@rollup/rollup-darwin-arm64@4.24.0':
    resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==}
    cpu: [arm64]
    os: [darwin]

  '@rollup/rollup-darwin-x64@4.24.0':
    resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==}
    cpu: [x64]
    os: [darwin]

  '@rollup/rollup-linux-arm-gnueabihf@4.24.0':
    resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==}
    cpu: [arm]
    os: [linux]

  '@rollup/rollup-linux-arm-musleabihf@4.24.0':
    resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==}
    cpu: [arm]
    os: [linux]

  '@rollup/rollup-linux-arm64-gnu@4.24.0':
    resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==}
    cpu: [arm64]
    os: [linux]

  '@rollup/rollup-linux-arm64-musl@4.24.0':
    resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==}
    cpu: [arm64]
    os: [linux]

  '@rollup/rollup-linux-powerpc64le-gnu@4.24.0':
    resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==}
    cpu: [ppc64]
    os: [linux]

  '@rollup/rollup-linux-riscv64-gnu@4.24.0':
    resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==}
    cpu: [riscv64]
    os: [linux]

  '@rollup/rollup-linux-s390x-gnu@4.24.0':
    resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==}
    cpu: [s390x]
    os: [linux]

  '@rollup/rollup-linux-x64-gnu@4.24.0':
    resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==}
    cpu: [x64]
    os: [linux]

  '@rollup/rollup-linux-x64-musl@4.24.0':
    resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==}
    cpu: [x64]
    os: [linux]

  '@rollup/rollup-win32-arm64-msvc@4.24.0':
    resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==}
    cpu: [arm64]
    os: [win32]

  '@rollup/rollup-win32-ia32-msvc@4.24.0':
    resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==}
    cpu: [ia32]
    os: [win32]

  '@rollup/rollup-win32-x64-msvc@4.24.0':
    resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==}
    cpu: [x64]
    os: [win32]

  '@swc/core-darwin-arm64@1.7.35':
    resolution: {integrity: sha512-BQSSozVxjxS+SVQz6e3GC/+OBWGIK3jfe52pWdANmycdjF3ch7lrCKTHTU7eHwyoJ96mofszPf5AsiVJF34Fwg==}
    engines: {node: '>=10'}
    cpu: [arm64]
    os: [darwin]

  '@swc/core-darwin-x64@1.7.35':
    resolution: {integrity: sha512-44TYdKN/EWtkU88foXR7IGki9JzhEJzaFOoPevfi9Xe7hjAD/x2+AJOWWqQNzDPMz9+QewLdUVLyR6s5okRgtg==}
    engines: {node: '>=10'}
    cpu: [x64]
    os: [darwin]

  '@swc/core-linux-arm-gnueabihf@1.7.35':
    resolution: {integrity: sha512-ccfA5h3zxwioD+/z/AmYtkwtKz9m4rWTV7RoHq6Jfsb0cXHrd6tbcvgqRWXra1kASlE+cDWsMtEZygs9dJRtUQ==}
    engines: {node: '>=10'}
    cpu: [arm]
    os: [linux]

  '@swc/core-linux-arm64-gnu@1.7.35':
    resolution: {integrity: sha512-hx65Qz+G4iG/IVtxJKewC5SJdki8PAPFGl6gC/57Jb0+jA4BIoGLD/J3Q3rCPeoHfdqpkCYpahtyUq8CKx41Jg==}
    engines: {node: '>=10'}
    cpu: [arm64]
    os: [linux]

  '@swc/core-linux-arm64-musl@1.7.35':
    resolution: {integrity: sha512-kL6tQL9No7UEoEvDRuPxzPTpxrvbwYteNRbdChSSP74j13/55G2/2hLmult5yFFaWuyoyU/2lvzjRL/i8OLZxg==}
    engines: {node: '>=10'}
    cpu: [arm64]
    os: [linux]

  '@swc/core-linux-x64-gnu@1.7.35':
    resolution: {integrity: sha512-Ke4rcLQSwCQ2LHdJX1FtnqmYNQ3IX6BddKlUtS7mcK13IHkQzZWp0Dcu6MgNA3twzb/dBpKX5GLy07XdGgfmyw==}
    engines: {node: '>=10'}
    cpu: [x64]
    os: [linux]

  '@swc/core-linux-x64-musl@1.7.35':
    resolution: {integrity: sha512-T30tlLnz0kYyDFyO5RQF5EQ4ENjW9+b56hEGgFUYmfhFhGA4E4V67iEx7KIG4u0whdPG7oy3qjyyIeTb7nElEw==}
    engines: {node: '>=10'}
    cpu: [x64]
    os: [linux]

  '@swc/core-win32-arm64-msvc@1.7.35':
    resolution: {integrity: sha512-CfM/k8mvtuMyX+okRhemfLt784PLS0KF7Q9djA8/Dtavk0L5Ghnq+XsGltO3d8B8+XZ7YOITsB14CrjehzeHsg==}
    engines: {node: '>=10'}
    cpu: [arm64]
    os: [win32]

  '@swc/core-win32-ia32-msvc@1.7.35':
    resolution: {integrity: sha512-ATB3uuH8j/RmS64EXQZJSbo2WXfRNpTnQszHME/sGaexsuxeijrp3DTYSFAA3R2Bu6HbIIX6jempe1Au8I3j+A==}
    engines: {node: '>=10'}
    cpu: [ia32]
    os: [win32]

  '@swc/core-win32-x64-msvc@1.7.35':
    resolution: {integrity: sha512-iDGfQO1571NqWUXtLYDhwIELA/wadH42ioGn+J9R336nWx40YICzy9UQyslWRhqzhQ5kT+QXAW/MoCWc058N6Q==}
    engines: {node: '>=10'}
    cpu: [x64]
    os: [win32]

  '@swc/core@1.7.35':
    resolution: {integrity: sha512-3cUteCTbr2r5jqfgx0r091sfq5Mgh6F1SQh8XAOnSvtKzwv2bC31mvBHVAieD1uPa2kHJhLav20DQgXOhpEitw==}
    engines: {node: '>=10'}
    peerDependencies:
      '@swc/helpers': '*'
    peerDependenciesMeta:
      '@swc/helpers':
        optional: true

  '@swc/counter@0.1.3':
    resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}

  '@swc/types@0.1.13':
    resolution: {integrity: sha512-JL7eeCk6zWCbiYQg2xQSdLXQJl8Qoc9rXmG2cEKvHe3CKwMHwHGpfOb8frzNLmbycOo6I51qxnLnn9ESf4I20Q==}

  '@types/estree@1.0.6':
    resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}

  '@types/json-schema@7.0.15':
    resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}

  '@types/prop-types@15.7.12':
    resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}

  '@types/react-dom@18.3.0':
    resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}

  '@types/react@18.3.3':
    resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==}

  '@types/stylis@4.2.5':
    resolution: {integrity: sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==}

  '@typescript-eslint/eslint-plugin@8.8.1':
    resolution: {integrity: sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
    peerDependencies:
      '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
      eslint: ^8.57.0 || ^9.0.0
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true

  '@typescript-eslint/parser@8.8.1':
    resolution: {integrity: sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
    peerDependencies:
      eslint: ^8.57.0 || ^9.0.0
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true

  '@typescript-eslint/scope-manager@8.8.1':
    resolution: {integrity: sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  '@typescript-eslint/type-utils@8.8.1':
    resolution: {integrity: sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
    peerDependencies:
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true

  '@typescript-eslint/types@8.8.1':
    resolution: {integrity: sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  '@typescript-eslint/typescript-estree@8.8.1':
    resolution: {integrity: sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
    peerDependencies:
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true

  '@typescript-eslint/utils@8.8.1':
    resolution: {integrity: sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
    peerDependencies:
      eslint: ^8.57.0 || ^9.0.0

  '@typescript-eslint/visitor-keys@8.8.1':
    resolution: {integrity: sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  '@vitejs/plugin-react-swc@3.7.1':
    resolution: {integrity: sha512-vgWOY0i1EROUK0Ctg1hwhtC3SdcDjZcdit4Ups4aPkDcB1jYhmo+RMYWY87cmXMhvtD5uf8lV89j2w16vkdSVg==}
    peerDependencies:
      vite: ^4 || ^5

  acorn-jsx@5.3.2:
    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
    peerDependencies:
      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0

  acorn@8.12.1:
    resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
    engines: {node: '>=0.4.0'}
    hasBin: true

  ajv@6.12.6:
    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}

  ansi-styles@4.3.0:
    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
    engines: {node: '>=8'}

  argparse@2.0.1:
    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}

  balanced-match@1.0.2:
    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}

  brace-expansion@1.1.11:
    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}

  brace-expansion@2.0.1:
    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}

  braces@3.0.3:
    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
    engines: {node: '>=8'}

  callsites@3.1.0:
    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
    engines: {node: '>=6'}

  camelize@1.0.1:
    resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}

  chalk@4.1.2:
    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
    engines: {node: '>=10'}

  color-convert@2.0.1:
    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
    engines: {node: '>=7.0.0'}

  color-name@1.1.4:
    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}

  concat-map@0.0.1:
    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}

  cross-spawn@7.0.3:
    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
    engines: {node: '>= 8'}

  css-color-keywords@1.0.0:
    resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
    engines: {node: '>=4'}

  css-to-react-native@3.2.0:
    resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}

  csstype@3.1.3:
    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}

  debug@4.3.5:
    resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
    engines: {node: '>=6.0'}
    peerDependencies:
      supports-color: '*'
    peerDependenciesMeta:
      supports-color:
        optional: true

  deep-is@0.1.4:
    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}

  esbuild@0.21.5:
    resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
    engines: {node: '>=12'}
    hasBin: true

  escape-string-regexp@4.0.0:
    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
    engines: {node: '>=10'}

  eslint-plugin-react-hooks@5.0.0:
    resolution: {integrity: sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==}
    engines: {node: '>=10'}
    peerDependencies:
      eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0

  eslint-plugin-react-refresh@0.4.12:
    resolution: {integrity: sha512-9neVjoGv20FwYtCP6CB1dzR1vr57ZDNOXst21wd2xJ/cTlM2xLq0GWVlSNTdMn/4BtP6cHYBMCSp1wFBJ9jBsg==}
    peerDependencies:
      eslint: '>=7'

  eslint-scope@8.1.0:
    resolution: {integrity: sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  eslint-visitor-keys@3.4.3:
    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}

  eslint-visitor-keys@4.1.0:
    resolution: {integrity: sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  eslint@9.12.0:
    resolution: {integrity: sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
    hasBin: true
    peerDependencies:
      jiti: '*'
    peerDependenciesMeta:
      jiti:
        optional: true

  espree@10.2.0:
    resolution: {integrity: sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==}
    engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}

  esquery@1.6.0:
    resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
    engines: {node: '>=0.10'}

  esrecurse@4.3.0:
    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
    engines: {node: '>=4.0'}

  estraverse@5.3.0:
    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
    engines: {node: '>=4.0'}

  esutils@2.0.3:
    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
    engines: {node: '>=0.10.0'}

  fast-deep-equal@3.1.3:
    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}

  fast-glob@3.3.2:
    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
    engines: {node: '>=8.6.0'}

  fast-json-stable-stringify@2.1.0:
    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}

  fast-levenshtein@2.0.6:
    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}

  fastq@1.17.1:
    resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}

  file-entry-cache@8.0.0:
    resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
    engines: {node: '>=16.0.0'}

  fill-range@7.1.1:
    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
    engines: {node: '>=8'}

  find-up@5.0.0:
    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
    engines: {node: '>=10'}

  flat-cache@4.0.1:
    resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
    engines: {node: '>=16'}

  flatted@3.3.1:
    resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}

  fsevents@2.3.3:
    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
    os: [darwin]

  glob-parent@5.1.2:
    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
    engines: {node: '>= 6'}

  glob-parent@6.0.2:
    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
    engines: {node: '>=10.13.0'}

  globals@14.0.0:
    resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
    engines: {node: '>=18'}

  graphemer@1.4.0:
    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}

  has-flag@4.0.0:
    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
    engines: {node: '>=8'}

  ignore@5.3.1:
    resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
    engines: {node: '>= 4'}

  import-fresh@3.3.0:
    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
    engines: {node: '>=6'}

  imurmurhash@0.1.4:
    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
    engines: {node: '>=0.8.19'}

  is-extglob@2.1.1:
    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
    engines: {node: '>=0.10.0'}

  is-glob@4.0.3:
    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
    engines: {node: '>=0.10.0'}

  is-number@7.0.0:
    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
    engines: {node: '>=0.12.0'}

  isexe@2.0.0:
    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}

  js-tokens@4.0.0:
    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}

  js-yaml@4.1.0:
    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
    hasBin: true

  json-buffer@3.0.1:
    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}

  json-schema-traverse@0.4.1:
    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}

  json-stable-stringify-without-jsonify@1.0.1:
    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}

  keyv@4.5.4:
    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}

  levn@0.4.1:
    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
    engines: {node: '>= 0.8.0'}

  locate-path@6.0.0:
    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
    engines: {node: '>=10'}

  lodash.merge@4.6.2:
    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}

  loose-envify@1.4.0:
    resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
    hasBin: true

  merge2@1.4.1:
    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
    engines: {node: '>= 8'}

  micromatch@4.0.7:
    resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
    engines: {node: '>=8.6'}

  minimatch@3.1.2:
    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}

  minimatch@9.0.5:
    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
    engines: {node: '>=16 || 14 >=14.17'}

  ms@2.1.2:
    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}

  nanoid@3.3.7:
    resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
    hasBin: true

  natural-compare@1.4.0:
    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}

  optionator@0.9.4:
    resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
    engines: {node: '>= 0.8.0'}

  p-limit@3.1.0:
    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
    engines: {node: '>=10'}

  p-locate@5.0.0:
    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
    engines: {node: '>=10'}

  parent-module@1.0.1:
    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
    engines: {node: '>=6'}

  path-exists@4.0.0:
    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
    engines: {node: '>=8'}

  path-key@3.1.1:
    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
    engines: {node: '>=8'}

  picocolors@1.0.1:
    resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}

  picocolors@1.1.0:
    resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==}

  picomatch@2.3.1:
    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
    engines: {node: '>=8.6'}

  postcss-value-parser@4.2.0:
    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}

  postcss@8.4.38:
    resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
    engines: {node: ^10 || ^12 || >=14}

  postcss@8.4.47:
    resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
    engines: {node: ^10 || ^12 || >=14}

  prelude-ls@1.2.1:
    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
    engines: {node: '>= 0.8.0'}

  punycode@2.3.1:
    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
    engines: {node: '>=6'}

  queue-microtask@1.2.3:
    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}

  react-dom@18.3.1:
    resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
    peerDependencies:
      react: ^18.3.1

  react@18.3.1:
    resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
    engines: {node: '>=0.10.0'}

  resolve-from@4.0.0:
    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
    engines: {node: '>=4'}

  reusify@1.0.4:
    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}

  rollup@4.24.0:
    resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==}
    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
    hasBin: true

  run-parallel@1.2.0:
    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}

  scheduler@0.23.2:
    resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}

  semver@7.6.2:
    resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==}
    engines: {node: '>=10'}
    hasBin: true

  shallowequal@1.1.0:
    resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}

  shebang-command@2.0.0:
    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
    engines: {node: '>=8'}

  shebang-regex@3.0.0:
    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
    engines: {node: '>=8'}

  source-map-js@1.2.0:
    resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
    engines: {node: '>=0.10.0'}

  source-map-js@1.2.1:
    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
    engines: {node: '>=0.10.0'}

  strip-json-comments@3.1.1:
    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
    engines: {node: '>=8'}

  styled-components@6.1.11:
    resolution: {integrity: sha512-Ui0jXPzbp1phYij90h12ksljKGqF8ncGx+pjrNPsSPhbUUjWT2tD1FwGo2LF6USCnbrsIhNngDfodhxbegfEOA==}
    engines: {node: '>= 16'}
    peerDependencies:
      react: '>= 16.8.0'
      react-dom: '>= 16.8.0'

  stylis@4.3.2:
    resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==}

  supports-color@7.2.0:
    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
    engines: {node: '>=8'}

  text-table@0.2.0:
    resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}

  to-regex-range@5.0.1:
    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
    engines: {node: '>=8.0'}

  ts-api-utils@1.3.0:
    resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
    engines: {node: '>=16'}
    peerDependencies:
      typescript: '>=4.2.0'

  tslib@2.6.2:
    resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}

  type-check@0.4.0:
    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
    engines: {node: '>= 0.8.0'}

  typescript@5.6.3:
    resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==}
    engines: {node: '>=14.17'}
    hasBin: true

  uri-js@4.4.1:
    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}

  vite@5.4.8:
    resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==}
    engines: {node: ^18.0.0 || >=20.0.0}
    hasBin: true
    peerDependencies:
      '@types/node': ^18.0.0 || >=20.0.0
      less: '*'
      lightningcss: ^1.21.0
      sass: '*'
      sass-embedded: '*'
      stylus: '*'
      sugarss: '*'
      terser: ^5.4.0
    peerDependenciesMeta:
      '@types/node':
        optional: true
      less:
        optional: true
      lightningcss:
        optional: true
      sass:
        optional: true
      sass-embedded:
        optional: true
      stylus:
        optional: true
      sugarss:
        optional: true
      terser:
        optional: true

  which@2.0.2:
    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
    engines: {node: '>= 8'}
    hasBin: true

  word-wrap@1.2.5:
    resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
    engines: {node: '>=0.10.0'}

  yocto-queue@0.1.0:
    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
    engines: {node: '>=10'}

snapshots:

  '@emotion/is-prop-valid@1.2.2':
    dependencies:
      '@emotion/memoize': 0.8.1

  '@emotion/memoize@0.8.1': {}

  '@emotion/unitless@0.8.1': {}

  '@esbuild/aix-ppc64@0.21.5':
    optional: true

  '@esbuild/android-arm64@0.21.5':
    optional: true

  '@esbuild/android-arm@0.21.5':
    optional: true

  '@esbuild/android-x64@0.21.5':
    optional: true

  '@esbuild/darwin-arm64@0.21.5':
    optional: true

  '@esbuild/darwin-x64@0.21.5':
    optional: true

  '@esbuild/freebsd-arm64@0.21.5':
    optional: true

  '@esbuild/freebsd-x64@0.21.5':
    optional: true

  '@esbuild/linux-arm64@0.21.5':
    optional: true

  '@esbuild/linux-arm@0.21.5':
    optional: true

  '@esbuild/linux-ia32@0.21.5':
    optional: true

  '@esbuild/linux-loong64@0.21.5':
    optional: true

  '@esbuild/linux-mips64el@0.21.5':
    optional: true

  '@esbuild/linux-ppc64@0.21.5':
    optional: true

  '@esbuild/linux-riscv64@0.21.5':
    optional: true

  '@esbuild/linux-s390x@0.21.5':
    optional: true

  '@esbuild/linux-x64@0.21.5':
    optional: true

  '@esbuild/netbsd-x64@0.21.5':
    optional: true

  '@esbuild/openbsd-x64@0.21.5':
    optional: true

  '@esbuild/sunos-x64@0.21.5':
    optional: true

  '@esbuild/win32-arm64@0.21.5':
    optional: true

  '@esbuild/win32-ia32@0.21.5':
    optional: true

  '@esbuild/win32-x64@0.21.5':
    optional: true

  '@eslint-community/eslint-utils@4.4.0(eslint@9.12.0)':
    dependencies:
      eslint: 9.12.0
      eslint-visitor-keys: 3.4.3

  '@eslint-community/regexpp@4.11.0': {}

  '@eslint/config-array@0.18.0':
    dependencies:
      '@eslint/object-schema': 2.1.4
      debug: 4.3.5
      minimatch: 3.1.2
    transitivePeerDependencies:
      - supports-color

  '@eslint/core@0.6.0': {}

  '@eslint/eslintrc@3.1.0':
    dependencies:
      ajv: 6.12.6
      debug: 4.3.5
      espree: 10.2.0
      globals: 14.0.0
      ignore: 5.3.1
      import-fresh: 3.3.0
      js-yaml: 4.1.0
      minimatch: 3.1.2
      strip-json-comments: 3.1.1
    transitivePeerDependencies:
      - supports-color

  '@eslint/js@9.12.0': {}

  '@eslint/object-schema@2.1.4': {}

  '@eslint/plugin-kit@0.2.0':
    dependencies:
      levn: 0.4.1

  '@humanfs/core@0.19.0': {}

  '@humanfs/node@0.16.5':
    dependencies:
      '@humanfs/core': 0.19.0
      '@humanwhocodes/retry': 0.3.1

  '@humanwhocodes/module-importer@1.0.1': {}

  '@humanwhocodes/retry@0.3.1': {}

  '@nodelib/fs.scandir@2.1.5':
    dependencies:
      '@nodelib/fs.stat': 2.0.5
      run-parallel: 1.2.0

  '@nodelib/fs.stat@2.0.5': {}

  '@nodelib/fs.walk@1.2.8':
    dependencies:
      '@nodelib/fs.scandir': 2.1.5
      fastq: 1.17.1

  '@rollup/rollup-android-arm-eabi@4.24.0':
    optional: true

  '@rollup/rollup-android-arm64@4.24.0':
    optional: true

  '@rollup/rollup-darwin-arm64@4.24.0':
    optional: true

  '@rollup/rollup-darwin-x64@4.24.0':
    optional: true

  '@rollup/rollup-linux-arm-gnueabihf@4.24.0':
    optional: true

  '@rollup/rollup-linux-arm-musleabihf@4.24.0':
    optional: true

  '@rollup/rollup-linux-arm64-gnu@4.24.0':
    optional: true

  '@rollup/rollup-linux-arm64-musl@4.24.0':
    optional: true

  '@rollup/rollup-linux-powerpc64le-gnu@4.24.0':
    optional: true

  '@rollup/rollup-linux-riscv64-gnu@4.24.0':
    optional: true

  '@rollup/rollup-linux-s390x-gnu@4.24.0':
    optional: true

  '@rollup/rollup-linux-x64-gnu@4.24.0':
    optional: true

  '@rollup/rollup-linux-x64-musl@4.24.0':
    optional: true

  '@rollup/rollup-win32-arm64-msvc@4.24.0':
    optional: true

  '@rollup/rollup-win32-ia32-msvc@4.24.0':
    optional: true

  '@rollup/rollup-win32-x64-msvc@4.24.0':
    optional: true

  '@swc/core-darwin-arm64@1.7.35':
    optional: true

  '@swc/core-darwin-x64@1.7.35':
    optional: true

  '@swc/core-linux-arm-gnueabihf@1.7.35':
    optional: true

  '@swc/core-linux-arm64-gnu@1.7.35':
    optional: true

  '@swc/core-linux-arm64-musl@1.7.35':
    optional: true

  '@swc/core-linux-x64-gnu@1.7.35':
    optional: true

  '@swc/core-linux-x64-musl@1.7.35':
    optional: true

  '@swc/core-win32-arm64-msvc@1.7.35':
    optional: true

  '@swc/core-win32-ia32-msvc@1.7.35':
    optional: true

  '@swc/core-win32-x64-msvc@1.7.35':
    optional: true

  '@swc/core@1.7.35':
    dependencies:
      '@swc/counter': 0.1.3
      '@swc/types': 0.1.13
    optionalDependencies:
      '@swc/core-darwin-arm64': 1.7.35
      '@swc/core-darwin-x64': 1.7.35
      '@swc/core-linux-arm-gnueabihf': 1.7.35
      '@swc/core-linux-arm64-gnu': 1.7.35
      '@swc/core-linux-arm64-musl': 1.7.35
      '@swc/core-linux-x64-gnu': 1.7.35
      '@swc/core-linux-x64-musl': 1.7.35
      '@swc/core-win32-arm64-msvc': 1.7.35
      '@swc/core-win32-ia32-msvc': 1.7.35
      '@swc/core-win32-x64-msvc': 1.7.35

  '@swc/counter@0.1.3': {}

  '@swc/types@0.1.13':
    dependencies:
      '@swc/counter': 0.1.3

  '@types/estree@1.0.6': {}

  '@types/json-schema@7.0.15': {}

  '@types/prop-types@15.7.12': {}

  '@types/react-dom@18.3.0':
    dependencies:
      '@types/react': 18.3.3

  '@types/react@18.3.3':
    dependencies:
      '@types/prop-types': 15.7.12
      csstype: 3.1.3

  '@types/stylis@4.2.5': {}

  '@typescript-eslint/eslint-plugin@8.8.1(@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.6.3))(eslint@9.12.0)(typescript@5.6.3)':
    dependencies:
      '@eslint-community/regexpp': 4.11.0
      '@typescript-eslint/parser': 8.8.1(eslint@9.12.0)(typescript@5.6.3)
      '@typescript-eslint/scope-manager': 8.8.1
      '@typescript-eslint/type-utils': 8.8.1(eslint@9.12.0)(typescript@5.6.3)
      '@typescript-eslint/utils': 8.8.1(eslint@9.12.0)(typescript@5.6.3)
      '@typescript-eslint/visitor-keys': 8.8.1
      eslint: 9.12.0
      graphemer: 1.4.0
      ignore: 5.3.1
      natural-compare: 1.4.0
      ts-api-utils: 1.3.0(typescript@5.6.3)
    optionalDependencies:
      typescript: 5.6.3
    transitivePeerDependencies:
      - supports-color

  '@typescript-eslint/parser@8.8.1(eslint@9.12.0)(typescript@5.6.3)':
    dependencies:
      '@typescript-eslint/scope-manager': 8.8.1
      '@typescript-eslint/types': 8.8.1
      '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3)
      '@typescript-eslint/visitor-keys': 8.8.1
      debug: 4.3.5
      eslint: 9.12.0
    optionalDependencies:
      typescript: 5.6.3
    transitivePeerDependencies:
      - supports-color

  '@typescript-eslint/scope-manager@8.8.1':
    dependencies:
      '@typescript-eslint/types': 8.8.1
      '@typescript-eslint/visitor-keys': 8.8.1

  '@typescript-eslint/type-utils@8.8.1(eslint@9.12.0)(typescript@5.6.3)':
    dependencies:
      '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3)
      '@typescript-eslint/utils': 8.8.1(eslint@9.12.0)(typescript@5.6.3)
      debug: 4.3.5
      ts-api-utils: 1.3.0(typescript@5.6.3)
    optionalDependencies:
      typescript: 5.6.3
    transitivePeerDependencies:
      - eslint
      - supports-color

  '@typescript-eslint/types@8.8.1': {}

  '@typescript-eslint/typescript-estree@8.8.1(typescript@5.6.3)':
    dependencies:
      '@typescript-eslint/types': 8.8.1
      '@typescript-eslint/visitor-keys': 8.8.1
      debug: 4.3.5
      fast-glob: 3.3.2
      is-glob: 4.0.3
      minimatch: 9.0.5
      semver: 7.6.2
      ts-api-utils: 1.3.0(typescript@5.6.3)
    optionalDependencies:
      typescript: 5.6.3
    transitivePeerDependencies:
      - supports-color

  '@typescript-eslint/utils@8.8.1(eslint@9.12.0)(typescript@5.6.3)':
    dependencies:
      '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0)
      '@typescript-eslint/scope-manager': 8.8.1
      '@typescript-eslint/types': 8.8.1
      '@typescript-eslint/typescript-estree': 8.8.1(typescript@5.6.3)
      eslint: 9.12.0
    transitivePeerDependencies:
      - supports-color
      - typescript

  '@typescript-eslint/visitor-keys@8.8.1':
    dependencies:
      '@typescript-eslint/types': 8.8.1
      eslint-visitor-keys: 3.4.3

  '@vitejs/plugin-react-swc@3.7.1(vite@5.4.8)':
    dependencies:
      '@swc/core': 1.7.35
      vite: 5.4.8
    transitivePeerDependencies:
      - '@swc/helpers'

  acorn-jsx@5.3.2(acorn@8.12.1):
    dependencies:
      acorn: 8.12.1

  acorn@8.12.1: {}

  ajv@6.12.6:
    dependencies:
      fast-deep-equal: 3.1.3
      fast-json-stable-stringify: 2.1.0
      json-schema-traverse: 0.4.1
      uri-js: 4.4.1

  ansi-styles@4.3.0:
    dependencies:
      color-convert: 2.0.1

  argparse@2.0.1: {}

  balanced-match@1.0.2: {}

  brace-expansion@1.1.11:
    dependencies:
      balanced-match: 1.0.2
      concat-map: 0.0.1

  brace-expansion@2.0.1:
    dependencies:
      balanced-match: 1.0.2

  braces@3.0.3:
    dependencies:
      fill-range: 7.1.1

  callsites@3.1.0: {}

  camelize@1.0.1: {}

  chalk@4.1.2:
    dependencies:
      ansi-styles: 4.3.0
      supports-color: 7.2.0

  color-convert@2.0.1:
    dependencies:
      color-name: 1.1.4

  color-name@1.1.4: {}

  concat-map@0.0.1: {}

  cross-spawn@7.0.3:
    dependencies:
      path-key: 3.1.1
      shebang-command: 2.0.0
      which: 2.0.2

  css-color-keywords@1.0.0: {}

  css-to-react-native@3.2.0:
    dependencies:
      camelize: 1.0.1
      css-color-keywords: 1.0.0
      postcss-value-parser: 4.2.0

  csstype@3.1.3: {}

  debug@4.3.5:
    dependencies:
      ms: 2.1.2

  deep-is@0.1.4: {}

  esbuild@0.21.5:
    optionalDependencies:
      '@esbuild/aix-ppc64': 0.21.5
      '@esbuild/android-arm': 0.21.5
      '@esbuild/android-arm64': 0.21.5
      '@esbuild/android-x64': 0.21.5
      '@esbuild/darwin-arm64': 0.21.5
      '@esbuild/darwin-x64': 0.21.5
      '@esbuild/freebsd-arm64': 0.21.5
      '@esbuild/freebsd-x64': 0.21.5
      '@esbuild/linux-arm': 0.21.5
      '@esbuild/linux-arm64': 0.21.5
      '@esbuild/linux-ia32': 0.21.5
      '@esbuild/linux-loong64': 0.21.5
      '@esbuild/linux-mips64el': 0.21.5
      '@esbuild/linux-ppc64': 0.21.5
      '@esbuild/linux-riscv64': 0.21.5
      '@esbuild/linux-s390x': 0.21.5
      '@esbuild/linux-x64': 0.21.5
      '@esbuild/netbsd-x64': 0.21.5
      '@esbuild/openbsd-x64': 0.21.5
      '@esbuild/sunos-x64': 0.21.5
      '@esbuild/win32-arm64': 0.21.5
      '@esbuild/win32-ia32': 0.21.5
      '@esbuild/win32-x64': 0.21.5

  escape-string-regexp@4.0.0: {}

  eslint-plugin-react-hooks@5.0.0(eslint@9.12.0):
    dependencies:
      eslint: 9.12.0

  eslint-plugin-react-refresh@0.4.12(eslint@9.12.0):
    dependencies:
      eslint: 9.12.0

  eslint-scope@8.1.0:
    dependencies:
      esrecurse: 4.3.0
      estraverse: 5.3.0

  eslint-visitor-keys@3.4.3: {}

  eslint-visitor-keys@4.1.0: {}

  eslint@9.12.0:
    dependencies:
      '@eslint-community/eslint-utils': 4.4.0(eslint@9.12.0)
      '@eslint-community/regexpp': 4.11.0
      '@eslint/config-array': 0.18.0
      '@eslint/core': 0.6.0
      '@eslint/eslintrc': 3.1.0
      '@eslint/js': 9.12.0
      '@eslint/plugin-kit': 0.2.0
      '@humanfs/node': 0.16.5
      '@humanwhocodes/module-importer': 1.0.1
      '@humanwhocodes/retry': 0.3.1
      '@types/estree': 1.0.6
      '@types/json-schema': 7.0.15
      ajv: 6.12.6
      chalk: 4.1.2
      cross-spawn: 7.0.3
      debug: 4.3.5
      escape-string-regexp: 4.0.0
      eslint-scope: 8.1.0
      eslint-visitor-keys: 4.1.0
      espree: 10.2.0
      esquery: 1.6.0
      esutils: 2.0.3
      fast-deep-equal: 3.1.3
      file-entry-cache: 8.0.0
      find-up: 5.0.0
      glob-parent: 6.0.2
      ignore: 5.3.1
      imurmurhash: 0.1.4
      is-glob: 4.0.3
      json-stable-stringify-without-jsonify: 1.0.1
      lodash.merge: 4.6.2
      minimatch: 3.1.2
      natural-compare: 1.4.0
      optionator: 0.9.4
      text-table: 0.2.0
    transitivePeerDependencies:
      - supports-color

  espree@10.2.0:
    dependencies:
      acorn: 8.12.1
      acorn-jsx: 5.3.2(acorn@8.12.1)
      eslint-visitor-keys: 4.1.0

  esquery@1.6.0:
    dependencies:
      estraverse: 5.3.0

  esrecurse@4.3.0:
    dependencies:
      estraverse: 5.3.0

  estraverse@5.3.0: {}

  esutils@2.0.3: {}

  fast-deep-equal@3.1.3: {}

  fast-glob@3.3.2:
    dependencies:
      '@nodelib/fs.stat': 2.0.5
      '@nodelib/fs.walk': 1.2.8
      glob-parent: 5.1.2
      merge2: 1.4.1
      micromatch: 4.0.7

  fast-json-stable-stringify@2.1.0: {}

  fast-levenshtein@2.0.6: {}

  fastq@1.17.1:
    dependencies:
      reusify: 1.0.4

  file-entry-cache@8.0.0:
    dependencies:
      flat-cache: 4.0.1

  fill-range@7.1.1:
    dependencies:
      to-regex-range: 5.0.1

  find-up@5.0.0:
    dependencies:
      locate-path: 6.0.0
      path-exists: 4.0.0

  flat-cache@4.0.1:
    dependencies:
      flatted: 3.3.1
      keyv: 4.5.4

  flatted@3.3.1: {}

  fsevents@2.3.3:
    optional: true

  glob-parent@5.1.2:
    dependencies:
      is-glob: 4.0.3

  glob-parent@6.0.2:
    dependencies:
      is-glob: 4.0.3

  globals@14.0.0: {}

  graphemer@1.4.0: {}

  has-flag@4.0.0: {}

  ignore@5.3.1: {}

  import-fresh@3.3.0:
    dependencies:
      parent-module: 1.0.1
      resolve-from: 4.0.0

  imurmurhash@0.1.4: {}

  is-extglob@2.1.1: {}

  is-glob@4.0.3:
    dependencies:
      is-extglob: 2.1.1

  is-number@7.0.0: {}

  isexe@2.0.0: {}

  js-tokens@4.0.0: {}

  js-yaml@4.1.0:
    dependencies:
      argparse: 2.0.1

  json-buffer@3.0.1: {}

  json-schema-traverse@0.4.1: {}

  json-stable-stringify-without-jsonify@1.0.1: {}

  keyv@4.5.4:
    dependencies:
      json-buffer: 3.0.1

  levn@0.4.1:
    dependencies:
      prelude-ls: 1.2.1
      type-check: 0.4.0

  locate-path@6.0.0:
    dependencies:
      p-locate: 5.0.0

  lodash.merge@4.6.2: {}

  loose-envify@1.4.0:
    dependencies:
      js-tokens: 4.0.0

  merge2@1.4.1: {}

  micromatch@4.0.7:
    dependencies:
      braces: 3.0.3
      picomatch: 2.3.1

  minimatch@3.1.2:
    dependencies:
      brace-expansion: 1.1.11

  minimatch@9.0.5:
    dependencies:
      brace-expansion: 2.0.1

  ms@2.1.2: {}

  nanoid@3.3.7: {}

  natural-compare@1.4.0: {}

  optionator@0.9.4:
    dependencies:
      deep-is: 0.1.4
      fast-levenshtein: 2.0.6
      levn: 0.4.1
      prelude-ls: 1.2.1
      type-check: 0.4.0
      word-wrap: 1.2.5

  p-limit@3.1.0:
    dependencies:
      yocto-queue: 0.1.0

  p-locate@5.0.0:
    dependencies:
      p-limit: 3.1.0

  parent-module@1.0.1:
    dependencies:
      callsites: 3.1.0

  path-exists@4.0.0: {}

  path-key@3.1.1: {}

  picocolors@1.0.1: {}

  picocolors@1.1.0: {}

  picomatch@2.3.1: {}

  postcss-value-parser@4.2.0: {}

  postcss@8.4.38:
    dependencies:
      nanoid: 3.3.7
      picocolors: 1.0.1
      source-map-js: 1.2.0

  postcss@8.4.47:
    dependencies:
      nanoid: 3.3.7
      picocolors: 1.1.0
      source-map-js: 1.2.1

  prelude-ls@1.2.1: {}

  punycode@2.3.1: {}

  queue-microtask@1.2.3: {}

  react-dom@18.3.1(react@18.3.1):
    dependencies:
      loose-envify: 1.4.0
      react: 18.3.1
      scheduler: 0.23.2

  react@18.3.1:
    dependencies:
      loose-envify: 1.4.0

  resolve-from@4.0.0: {}

  reusify@1.0.4: {}

  rollup@4.24.0:
    dependencies:
      '@types/estree': 1.0.6
    optionalDependencies:
      '@rollup/rollup-android-arm-eabi': 4.24.0
      '@rollup/rollup-android-arm64': 4.24.0
      '@rollup/rollup-darwin-arm64': 4.24.0
      '@rollup/rollup-darwin-x64': 4.24.0
      '@rollup/rollup-linux-arm-gnueabihf': 4.24.0
      '@rollup/rollup-linux-arm-musleabihf': 4.24.0
      '@rollup/rollup-linux-arm64-gnu': 4.24.0
      '@rollup/rollup-linux-arm64-musl': 4.24.0
      '@rollup/rollup-linux-powerpc64le-gnu': 4.24.0
      '@rollup/rollup-linux-riscv64-gnu': 4.24.0
      '@rollup/rollup-linux-s390x-gnu': 4.24.0
      '@rollup/rollup-linux-x64-gnu': 4.24.0
      '@rollup/rollup-linux-x64-musl': 4.24.0
      '@rollup/rollup-win32-arm64-msvc': 4.24.0
      '@rollup/rollup-win32-ia32-msvc': 4.24.0
      '@rollup/rollup-win32-x64-msvc': 4.24.0
      fsevents: 2.3.3

  run-parallel@1.2.0:
    dependencies:
      queue-microtask: 1.2.3

  scheduler@0.23.2:
    dependencies:
      loose-envify: 1.4.0

  semver@7.6.2: {}

  shallowequal@1.1.0: {}

  shebang-command@2.0.0:
    dependencies:
      shebang-regex: 3.0.0

  shebang-regex@3.0.0: {}

  source-map-js@1.2.0: {}

  source-map-js@1.2.1: {}

  strip-json-comments@3.1.1: {}

  styled-components@6.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
    dependencies:
      '@emotion/is-prop-valid': 1.2.2
      '@emotion/unitless': 0.8.1
      '@types/stylis': 4.2.5
      css-to-react-native: 3.2.0
      csstype: 3.1.3
      postcss: 8.4.38
      react: 18.3.1
      react-dom: 18.3.1(react@18.3.1)
      shallowequal: 1.1.0
      stylis: 4.3.2
      tslib: 2.6.2

  stylis@4.3.2: {}

  supports-color@7.2.0:
    dependencies:
      has-flag: 4.0.0

  text-table@0.2.0: {}

  to-regex-range@5.0.1:
    dependencies:
      is-number: 7.0.0

  ts-api-utils@1.3.0(typescript@5.6.3):
    dependencies:
      typescript: 5.6.3

  tslib@2.6.2: {}

  type-check@0.4.0:
    dependencies:
      prelude-ls: 1.2.1

  typescript@5.6.3: {}

  uri-js@4.4.1:
    dependencies:
      punycode: 2.3.1

  vite@5.4.8:
    dependencies:
      esbuild: 0.21.5
      postcss: 8.4.47
      rollup: 4.24.0
    optionalDependencies:
      fsevents: 2.3.3

  which@2.0.2:
    dependencies:
      isexe: 2.0.0

  word-wrap@1.2.5: {}

  yocto-queue@0.1.0: {}
07070100000042000081A4000000000000000000000001680FB1F800000000000000000000000000000000000000000000002600000000kube-score-1.20.0/pnpm-workspace.yaml07070100000043000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001B00000000kube-score-1.20.0/renderer07070100000044000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001E00000000kube-score-1.20.0/renderer/ci07070100000045000081A4000000000000000000000001680FB1F80000051F000000000000000000000000000000000000002400000000kube-score-1.20.0/renderer/ci/ci.go// Package ci is currently considered to be in alpha status, and is not covered
// by the API stability guarantees
package ci

import (
	"bytes"
	"fmt"
	"io"
	"sort"

	"github.com/zegl/kube-score/scorecard"
)

// "Machine" / CI friendly output
func CI(scoreCard *scorecard.Scorecard) io.Reader {
	w := bytes.NewBufferString("")

	// Print the items sorted by scorecard key
	var keys []string
	for k := range *scoreCard {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	for _, key := range keys {
		scoredObject := (*scoreCard)[key]

		for _, card := range scoredObject.Checks {
			if len(card.Comments) == 0 {
				if card.Skipped {
					fmt.Fprintf(w, "[SKIPPED] %s\n",
						scoredObject.HumanFriendlyRef(),
					)
				} else {
					fmt.Fprintf(w, "[%s] %s\n",
						card.Grade.String(),
						scoredObject.HumanFriendlyRef(),
					)
				}
			}

			for _, comment := range card.Comments {
				message := comment.Summary
				if comment.Path != "" {
					message = "(" + comment.Path + ") " + comment.Summary
				}

				if card.Skipped {
					fmt.Fprintf(w, "[SKIPPED] %s: %s\n",
						scoredObject.HumanFriendlyRef(),
						message,
					)
				} else {
					fmt.Fprintf(w, "[%s] %s: %s\n",
						card.Grade.String(),
						scoredObject.HumanFriendlyRef(),
						message,
					)
				}
			}
		}
	}

	return w
}
07070100000046000081A4000000000000000000000001680FB1F800000907000000000000000000000000000000000000002900000000kube-score-1.20.0/renderer/ci/ci_test.gopackage ci

import (
	"io"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func getTestCard() *scorecard.Scorecard {
	checks := []scorecard.TestScore{
		{
			Check: domain.Check{
				Name: "test-warning-two-comments",
			},
			Grade: scorecard.GradeWarning,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "summary",
					Description: "description",
				},
				{
					// No path
					Summary:     "summary",
					Description: "description",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-ok-comment",
			},
			Grade: scorecard.GradeAllOK,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "summary",
					Description: "description",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-skipped-comment",
			},
			Skipped: true,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "skipped sum",
					Description: "skipped description",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-skipped-no-comment",
			},
			Skipped: true,
		},
	}

	return &scorecard.Scorecard{
		"a": &scorecard.ScoredObject{
			TypeMeta: v1.TypeMeta{
				Kind:       "Testing",
				APIVersion: "v1",
			},
			ObjectMeta: v1.ObjectMeta{
				Name:      "foo",
				Namespace: "foofoo",
			},
			Checks: checks,
		},

		// No namespace
		"b": &scorecard.ScoredObject{
			TypeMeta: v1.TypeMeta{
				Kind:       "Testing",
				APIVersion: "v1",
			},
			ObjectMeta: v1.ObjectMeta{
				Name: "bar-no-namespace",
			},
			Checks: checks,
		},
	}
}

func TestCiOutput(t *testing.T) {
	t.Parallel()
	// Defaults
	r := CI(getTestCard())
	all, err := io.ReadAll(r)
	assert.Nil(t, err)
	assert.Equal(t, `[WARNING] foo/foofoo v1/Testing: (a) summary
[WARNING] foo/foofoo v1/Testing: summary
[OK] foo/foofoo v1/Testing: (a) summary
[SKIPPED] foo/foofoo v1/Testing: (a) skipped sum
[SKIPPED] foo/foofoo v1/Testing
[WARNING] bar-no-namespace v1/Testing: (a) summary
[WARNING] bar-no-namespace v1/Testing: summary
[OK] bar-no-namespace v1/Testing: (a) summary
[SKIPPED] bar-no-namespace v1/Testing: (a) skipped sum
[SKIPPED] bar-no-namespace v1/Testing
`, string(all))
}
07070100000047000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002100000000kube-score-1.20.0/renderer/human07070100000048000081A4000000000000000000000001680FB1F800000F8E000000000000000000000000000000000000002A00000000kube-score-1.20.0/renderer/human/human.go// Package human is currently considered to be in alpha status, and is not covered
// by the API stability guarantees
package human

import (
	"bytes"
	"fmt"
	"io"
	"sort"
	"strings"

	"github.com/eidolon/wordwrap"
	"github.com/fatih/color"

	"github.com/zegl/kube-score/scorecard"
)

func Human(scoreCard *scorecard.Scorecard, verboseOutput int, termWidth int, useColors bool) (io.Reader, error) {
	// Print the items sorted by scorecard key
	var keys []string
	for k := range *scoreCard {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// Override usage of colors to our own preference
	color.NoColor = !useColors

	w := bytes.NewBufferString("")

	for _, key := range keys {
		scoredObject := (*scoreCard)[key]

		// Headers for each object
		var writtenHeaderChars int
		writtenHeaderChars, _ = color.New(color.FgMagenta).Fprintf(w, "%s/%s %s", scoredObject.TypeMeta.APIVersion, scoredObject.TypeMeta.Kind, scoredObject.ObjectMeta.Name)
		if scoredObject.ObjectMeta.Namespace != "" {
			written2, _ := color.New(color.FgMagenta).Fprintf(w, " in %s", scoredObject.ObjectMeta.Namespace)
			writtenHeaderChars += written2
		}

		// Adjust to termsize
		_, err := fmt.Fprint(w, safeRepeat(" ", min(80, termWidth)-writtenHeaderChars-2))
		if err != nil {
			return nil, fmt.Errorf("failed to write terminal padding: %w", err)
		}

		switch {
		case scoredObject.AnyBelowOrEqualToGrade(scorecard.GradeCritical):
			_, err = fmt.Fprintf(w, "💥\n")
		case scoredObject.AnyBelowOrEqualToGrade(scorecard.GradeWarning):
			_, err = fmt.Fprintf(w, "🤔\n")
		default:
			_, err = fmt.Fprintf(w, "✅\n")
		}
		if err != nil {
			return nil, fmt.Errorf("failed to write: %w", err)
		}

		// Display file name if the object has any warnings or criticals
		if scoredObject.AnyBelowOrEqualToGrade(scorecard.GradeWarning) {
			if scoredObject.FileLocation.Name != "" {
				_, _ = color.New(color.FgHiBlack).Fprintf(w, "    path=%s\n", scoredObject.FileLocation.Name)
			}
		}

		for _, card := range scoredObject.Checks {
			r := outputHumanStep(card, verboseOutput, termWidth)
			if _, err := io.Copy(w, r); err != nil {
				return nil, fmt.Errorf("failed to copy output: %w", err)
			}
		}
	}

	return w, nil
}

func outputHumanStep(card scorecard.TestScore, verboseOutput int, termWidth int) io.Reader {
	w := bytes.NewBufferString("")

	// Only print skipped items if verbosity is at least 2
	if card.Skipped && verboseOutput < 2 {
		return w
	}

	var col color.Attribute

	switch {
	case card.Skipped || card.Grade >= scorecard.GradeAllOK:
		// Higher than or equal to --threshold-ok
		col = color.FgGreen

		// If verbose output is disabled, skip OK items in the output
		if verboseOutput == 0 {
			return w
		}

	case card.Grade >= scorecard.GradeWarning:
		// Higher than or equal to --threshold-warning
		col = color.FgYellow
	default:
		// All lower than both --threshold-ok and --threshold-warning are critical
		col = color.FgRed
	}

	if card.Skipped {
		color.New(col).Fprintf(w, "    [SKIPPED] %s\n", card.Check.Name)
	} else {
		color.New(col).Fprintf(w, "    [%s] %s\n", card.Grade.String(), card.Check.Name)
	}

	for _, comment := range card.Comments {
		fmt.Fprintf(w, "        · ")

		if len(comment.Path) > 0 {
			fmt.Fprintf(w, "%s -> ", comment.Path)
		}

		fmt.Fprint(w, comment.Summary)

		if len(comment.Description) > 0 {
			wrapWidth := termWidth - 12
			if wrapWidth < 40 {
				wrapWidth = 40
			}
			wrapper := wordwrap.Wrapper(wrapWidth, false)
			wrapped := wrapper(comment.Description)
			fmt.Fprintln(w)
			fmt.Fprint(w, wordwrap.Indent(wrapped, strings.Repeat(" ", 12), false))
		}

		if len(comment.DocumentationURL) > 0 {
			fmt.Fprintln(w)
			fmt.Fprintf(w, "%sMore information: %s", strings.Repeat(" ", 12), comment.DocumentationURL)
		}

		fmt.Fprintln(w)
	}

	return w
}

func safeRepeat(s string, count int) string {
	if count < 0 {
		return ""
	}
	return strings.Repeat(s, count)
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
07070100000049000081A4000000000000000000000001680FB1F800003214000000000000000000000000000000000000003000000000kube-score-1.20.0/renderer/human/output_test.gopackage human

import (
	"io"
	"testing"

	"github.com/stretchr/testify/assert"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
)

func getTestCard() *scorecard.Scorecard {
	checks := []scorecard.TestScore{
		{
			Check: domain.Check{
				Name: "test-warning-two-comments",
			},
			Grade: scorecard.GradeWarning,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "summary",
					Description: "description",
				},
				{
					// No path
					Summary:          "summary",
					Description:      "description",
					DocumentationURL: "https://kube-score.com/whatever",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-ok-comment",
			},
			Grade: scorecard.GradeAllOK,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "summary",
					Description: "description",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-skipped-comment",
			},
			Skipped: true,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "skipped sum",
					Description: "skipped description",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-skipped-no-comment",
			},
			Skipped: true,
		},
	}

	return &scorecard.Scorecard{
		"a": &scorecard.ScoredObject{
			TypeMeta: v1.TypeMeta{
				Kind:       "Testing",
				APIVersion: "v1",
			},
			ObjectMeta: v1.ObjectMeta{
				Name:      "foo",
				Namespace: "foofoo",
			},
			Checks: checks,
		},

		// No namespace
		"b": &scorecard.ScoredObject{
			TypeMeta: v1.TypeMeta{
				Kind:       "Testing",
				APIVersion: "v1",
			},
			ObjectMeta: v1.ObjectMeta{
				Name: "bar-no-namespace",
			},
			Checks: checks,
		},
	}
}

func TestHumanOutputDefault(t *testing.T) {
	t.Parallel()
	r, err := Human(getTestCard(), 0, 100, false)
	assert.Nil(t, err)
	all, err := io.ReadAll(r)
	assert.Nil(t, err)
	assert.Equal(t, `v1/Testing foo in foofoo                                                      🤔
    [WARNING] test-warning-two-comments
        · a -> summary
            description
        · summary
            description
            More information: https://kube-score.com/whatever
v1/Testing bar-no-namespace                                                   🤔
    [WARNING] test-warning-two-comments
        · a -> summary
            description
        · summary
            description
            More information: https://kube-score.com/whatever
`, string(all))
}

func TestHumanOutputVerbose1(t *testing.T) {
	t.Parallel()
	r, err := Human(getTestCard(), 1, 100, false)
	assert.Nil(t, err)
	all, err := io.ReadAll(r)
	assert.Nil(t, err)
	assert.Equal(t, `v1/Testing foo in foofoo                                                      🤔
    [WARNING] test-warning-two-comments
        · a -> summary
            description
        · summary
            description
            More information: https://kube-score.com/whatever
    [OK] test-ok-comment
        · a -> summary
            description
v1/Testing bar-no-namespace                                                   🤔
    [WARNING] test-warning-two-comments
        · a -> summary
            description
        · summary
            description
            More information: https://kube-score.com/whatever
    [OK] test-ok-comment
        · a -> summary
            description
`, string(all))
}

func TestHumanOutputVerbose2(t *testing.T) {
	t.Parallel()
	r, err := Human(getTestCard(), 2, 100, false)
	assert.Nil(t, err)
	all, err := io.ReadAll(r)
	assert.Nil(t, err)
	assert.Equal(t, `v1/Testing foo in foofoo                                                      🤔
    [WARNING] test-warning-two-comments
        · a -> summary
            description
        · summary
            description
            More information: https://kube-score.com/whatever
    [OK] test-ok-comment
        · a -> summary
            description
    [SKIPPED] test-skipped-comment
        · a -> skipped sum
            skipped description
    [SKIPPED] test-skipped-no-comment
v1/Testing bar-no-namespace                                                   🤔
    [WARNING] test-warning-two-comments
        · a -> summary
            description
        · summary
            description
            More information: https://kube-score.com/whatever
    [OK] test-ok-comment
        · a -> summary
            description
    [SKIPPED] test-skipped-comment
        · a -> skipped sum
            skipped description
    [SKIPPED] test-skipped-no-comment
`, string(all))
}

func getTestCardAllOK() *scorecard.Scorecard {
	checks := []scorecard.TestScore{
		{
			Check: domain.Check{
				Name: "test-warning-two-comments",
			},
			Grade: scorecard.GradeAllOK,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "summary",
					Description: "description",
				},
				{
					// No path
					Summary:     "summary",
					Description: "description",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-ok-comment",
			},
			Grade: scorecard.GradeAllOK,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "summary",
					Description: "description",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-skipped-comment",
			},
			Skipped: true,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "skipped sum",
					Description: "skipped description",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-skipped-no-comment",
			},
			Skipped: true,
		},
	}

	return &scorecard.Scorecard{
		"a": &scorecard.ScoredObject{
			TypeMeta: v1.TypeMeta{
				Kind:       "Testing",
				APIVersion: "v1",
			},
			ObjectMeta: v1.ObjectMeta{
				Name:      "foo",
				Namespace: "foofoo",
			},
			Checks: checks,
		},

		// No namespace
		"b": &scorecard.ScoredObject{
			TypeMeta: v1.TypeMeta{
				Kind:       "Testing",
				APIVersion: "v1",
			},
			ObjectMeta: v1.ObjectMeta{
				Name: "bar-no-namespace",
			},
			Checks: checks,
		},
	}
}

func TestHumanOutputAllOKDefault(t *testing.T) {
	t.Parallel()
	r, err := Human(getTestCardAllOK(), 0, 100, false)
	assert.Nil(t, err)
	all, err := io.ReadAll(r)
	assert.Nil(t, err)
	assert.Equal(t, `v1/Testing foo in foofoo                                                      ✅
v1/Testing bar-no-namespace                                                   ✅
`, string(all))
}

func getTestCardLongDescription() *scorecard.Scorecard {
	checks := []scorecard.TestScore{
		{
			Check: domain.Check{
				Name: "test-warning-two-comments",
			},
			Grade: scorecard.GradeWarning,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "summary",
					Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras elementum sagittis lacus, a dictum tortor lobortis vel. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla eu neque erat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas et nisl venenatis, elementum augue a, porttitor libero.",
				},
			},
		},
	}

	return &scorecard.Scorecard{
		"a": &scorecard.ScoredObject{
			TypeMeta: v1.TypeMeta{
				Kind:       "Testing",
				APIVersion: "v1",
			},
			ObjectMeta: v1.ObjectMeta{
				Name:      "foo",
				Namespace: "foofoo",
			},
			Checks: checks,
		},
	}
}

func TestHumanOutputLogDescription120Width(t *testing.T) {
	t.Parallel()
	r, err := Human(getTestCardLongDescription(), 0, 120, false)
	assert.Nil(t, err)
	all, err := io.ReadAll(r)
	assert.Nil(t, err)
	assert.Equal(t, `v1/Testing foo in foofoo                                                      🤔
    [WARNING] test-warning-two-comments
        · a -> summary
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras elementum sagittis lacus, a dictum tortor
            lobortis vel. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
            Nulla eu neque erat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;
            Maecenas et nisl venenatis, elementum augue a, porttitor libero.
`, string(all))
}

func TestHumanOutputLogDescription100Width(t *testing.T) {
	t.Parallel()
	r, err := Human(getTestCardLongDescription(), 0, 100, false)
	assert.Nil(t, err)
	all, err := io.ReadAll(r)
	assert.Nil(t, err)
	assert.Equal(t, `v1/Testing foo in foofoo                                                      🤔
    [WARNING] test-warning-two-comments
        · a -> summary
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras elementum sagittis lacus,
            a dictum tortor lobortis vel. Pellentesque habitant morbi tristique senectus et netus et
            malesuada fames ac turpis egestas. Nulla eu neque erat. Vestibulum ante ipsum primis in
            faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas et nisl venenatis,
            elementum augue a, porttitor libero.
`, string(all))
}

func TestHumanOutputLogDescription80Width(t *testing.T) {
	t.Parallel()
	r, err := Human(getTestCardLongDescription(), 0, 80, false)
	assert.Nil(t, err)
	all, err := io.ReadAll(r)
	assert.Nil(t, err)
	assert.Equal(t, `v1/Testing foo in foofoo                                                      🤔
    [WARNING] test-warning-two-comments
        · a -> summary
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras
            elementum sagittis lacus, a dictum tortor lobortis vel. Pellentesque
            habitant morbi tristique senectus et netus et malesuada fames ac
            turpis egestas. Nulla eu neque erat. Vestibulum ante ipsum primis in
            faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas et
            nisl venenatis, elementum augue a, porttitor libero.
`, string(all))
}

func TestHumanOutputLogDescription0Width(t *testing.T) {
	t.Parallel()
	r, err := Human(getTestCardLongDescription(), 0, 0, false)
	assert.Nil(t, err)
	all, err := io.ReadAll(r)
	assert.Nil(t, err)
	assert.Equal(t, `v1/Testing foo in foofoo🤔
    [WARNING] test-warning-two-comments
        · a -> summary
            Lorem ipsum dolor sit amet, consectetur
            adipiscing elit. Cras elementum sagittis
            lacus, a dictum tortor lobortis vel.
            Pellentesque habitant morbi tristique
            senectus et netus et malesuada fames ac
            turpis egestas. Nulla eu neque erat.
            Vestibulum ante ipsum primis in faucibus
            orci luctus et ultrices posuere cubilia
            Curae; Maecenas et nisl venenatis,
            elementum augue a, porttitor libero.
`, string(all))
}

func getTestCardLongTitle() *scorecard.Scorecard {
	checks := []scorecard.TestScore{
		{
			Check: domain.Check{
				Name: "test-warning-two-comments",
			},
			Grade: scorecard.GradeWarning,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "summary",
					Description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras elementum sagittis lacus, a dictum tortor lobortis vel. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla eu neque erat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas et nisl venenatis, elementum augue a, porttitor libero.",
				},
			},
		},
	}

	return &scorecard.Scorecard{
		"a": &scorecard.ScoredObject{
			TypeMeta: v1.TypeMeta{
				Kind:       "Testing",
				APIVersion: "v1",
			},
			ObjectMeta: v1.ObjectMeta{
				Name:      "this-is-a-very-long-title-this-is-a-very-long-title-this-is-a-very-long-title-this-is-a-very-long-title-this-is-a-very-long-title",
				Namespace: "foofoo",
			},
			Checks: checks,
		},
	}
}

func TestHumanOutputWithLongObjectNames(t *testing.T) {
	t.Parallel()
	r, err := Human(getTestCardLongTitle(), 0, 80, false)
	assert.Nil(t, err)
	all, err := io.ReadAll(r)
	assert.Nil(t, err)
	assert.Equal(t, `v1/Testing this-is-a-very-long-title-this-is-a-very-long-title-this-is-a-very-long-title-this-is-a-very-long-title-this-is-a-very-long-title in foofoo🤔
    [WARNING] test-warning-two-comments
        · a -> summary
            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras
            elementum sagittis lacus, a dictum tortor lobortis vel. Pellentesque
            habitant morbi tristique senectus et netus et malesuada fames ac
            turpis egestas. Nulla eu neque erat. Vestibulum ante ipsum primis in
            faucibus orci luctus et ultrices posuere cubilia Curae; Maecenas et
            nisl venenatis, elementum augue a, porttitor libero.
`, string(all))
}
0707010000004A000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002300000000kube-score-1.20.0/renderer/json_v20707010000004B000081A4000000000000000000000001680FB1F8000008A0000000000000000000000000000000000000002A00000000kube-score-1.20.0/renderer/json_v2/api.gopackage json_v2

import (
	"bytes"
	"encoding/json"
	"io"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
)

type Check struct {
	Name       string `json:"name"`
	ID         string `json:"id"`
	TargetType string `json:"target_type"`
	Comment    string `json:"comment"`
	Optional   bool   `json:"optional"`
}

type ScoredObject struct {
	ObjectName string            `json:"object_name"`
	TypeMeta   metav1.TypeMeta   `json:"type_meta"`
	ObjectMeta metav1.ObjectMeta `json:"object_meta"`
	Checks     []TestScore       `json:"checks"`
	FileName   string            `json:"file_name"`
	FileRow    int               `json:"file_row"`
}

type TestScore struct {
	Check    Check              `json:"check"`
	Grade    scorecard.Grade    `json:"grade"`
	Skipped  bool               `json:"skipped"`
	Comments []TestScoreComment `json:"comments"`
}

type TestScoreComment struct {
	Path        string `json:"path"`
	Summary     string `json:"summary"`
	Description string `json:"description"`
}

func Output(input *scorecard.Scorecard) io.Reader {
	var objs []ScoredObject

	for k, v := range *input {
		objs = append(objs, ScoredObject{
			ObjectName: k,
			TypeMeta:   v.TypeMeta,
			ObjectMeta: v.ObjectMeta,
			Checks:     convertTestScore(v.Checks),
			FileName:   v.FileLocation.Name,
			FileRow:    v.FileLocation.Line,
		})
	}

	j, err := json.MarshalIndent(objs, "", "    ")
	if err != nil {
		panic(err)
	}
	return bytes.NewBuffer(j)
}

func convertTestScore(in []scorecard.TestScore) (res []TestScore) {
	for _, v := range in {
		res = append(res, TestScore{
			Check:    convertCheck(v.Check),
			Grade:    v.Grade,
			Skipped:  v.Skipped,
			Comments: convertComments(v.Comments),
		})
	}
	return
}

func convertComments(in []scorecard.TestScoreComment) (res []TestScoreComment) {
	for _, v := range in {
		res = append(res, TestScoreComment{
			Path:        v.Path,
			Summary:     v.Summary,
			Description: v.Description,
		})
	}
	return
}

func convertCheck(v ks.Check) Check {
	return Check{
		Name:       v.Name,
		ID:         v.ID,
		TargetType: v.TargetType,
		Comment:    v.Comment,
		Optional:   v.Optional,
	}
}
0707010000004C000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002100000000kube-score-1.20.0/renderer/junit0707010000004D000081A4000000000000000000000001680FB1F8000008F6000000000000000000000000000000000000002A00000000kube-score-1.20.0/renderer/junit/junit.gopackage junit

import (
	"bytes"
	"io"

	"github.com/jstemmer/go-junit-report/v2/junit"
	"github.com/zegl/kube-score/scorecard"
)

// JUnit XML output
func JUnit(scoreCard *scorecard.Scorecard) io.Reader {
	testSuites := junit.Testsuites{
		Name: "kube-score",
	}

	for _, scoredObject := range *scoreCard {
		testsuite := junit.Testsuite{
			Name: scoredObject.HumanFriendlyRef(),
		}

		for _, testScore := range scoredObject.Checks {
			if len(testScore.Comments) == 0 {
				if testScore.Skipped {
					testsuite.AddTestcase(junit.Testcase{
						Name:      testScore.Check.Name,
						Classname: scoredObject.HumanFriendlyRef(),
						Skipped:   &junit.Result{},
					})
				} else {
					if testScore.Grade == scorecard.GradeAlmostOK || testScore.Grade == scorecard.GradeAllOK {
						testsuite.AddTestcase(junit.Testcase{
							Name:      testScore.Check.Name,
							Classname: scoredObject.HumanFriendlyRef(),
						})
					} else {
						testsuite.AddTestcase(junit.Testcase{
							Name:      testScore.Check.Name,
							Classname: scoredObject.HumanFriendlyRef(),
							Failure:   &junit.Result{},
						})
					}
				}
			} else {
				for _, comment := range testScore.Comments {
					message := comment.Summary + ": " + comment.Description
					if comment.Path != "" {
						message = "(" + comment.Path + ") " + comment.Summary + ": " + comment.Description
					}

					if testScore.Skipped {
						testsuite.AddTestcase(junit.Testcase{
							Name:      testScore.Check.Name,
							Classname: scoredObject.HumanFriendlyRef(),
							Skipped: &junit.Result{
								Message: message,
							},
						})
					} else {
						if testScore.Grade == scorecard.GradeAlmostOK || testScore.Grade == scorecard.GradeAllOK {
							testsuite.AddTestcase(junit.Testcase{
								Name:      testScore.Check.Name,
								Classname: scoredObject.HumanFriendlyRef(),
							})
						} else {
							testsuite.AddTestcase(junit.Testcase{
								Name:      testScore.Check.Name,
								Classname: scoredObject.HumanFriendlyRef(),
								Failure: &junit.Result{
									Message: message,
								},
							})
						}

					}
				}
			}
		}
		testSuites.AddSuite(testsuite)
	}

	buffer := &bytes.Buffer{}
	err := testSuites.WriteXML(buffer)
	if err != nil {
		panic(err)
	}
	return buffer
}
0707010000004E000081A4000000000000000000000001680FB1F800000DDA000000000000000000000000000000000000002F00000000kube-score-1.20.0/renderer/junit/junit_test.gopackage junit

import (
	"io"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func getTestCard() *scorecard.Scorecard {
	checks := []scorecard.TestScore{
		{
			Check: domain.Check{
				Name: "test-warning-two-comments",
			},
			Grade: scorecard.GradeWarning,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "summary",
					Description: "description",
				},
				{
					// No path
					Summary:     "summary",
					Description: "description",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-ok-comment",
			},
			Grade: scorecard.GradeAllOK,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "summary",
					Description: "description",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-skipped-comment",
			},
			Skipped: true,
			Comments: []scorecard.TestScoreComment{
				{
					Path:        "a",
					Summary:     "skipped sum",
					Description: "skipped description",
				},
			},
		},
		{
			Check: domain.Check{
				Name: "test-skipped-no-comment",
			},
			Skipped: true,
		},
	}

	return &scorecard.Scorecard{
		"a": &scorecard.ScoredObject{
			TypeMeta: v1.TypeMeta{
				Kind:       "Testing",
				APIVersion: "v1",
			},
			ObjectMeta: v1.ObjectMeta{
				Name:      "foo",
				Namespace: "foofoo",
			},
			Checks: checks,
		},

		// No namespace
		"b": &scorecard.ScoredObject{
			TypeMeta: v1.TypeMeta{
				Kind:       "Testing",
				APIVersion: "v1",
			},
			ObjectMeta: v1.ObjectMeta{
				Name: "bar-no-namespace",
			},
			Checks: checks,
		},
	}
}

func TestJUnitOutput(t *testing.T) {
	t.Parallel()
	r := JUnit(getTestCard())
	all, err := io.ReadAll(r)
	assert.Nil(t, err)
	assert.Equal(t, `<testsuites name="kube-score" tests="10" failures="4" skipped="4">
	<testsuite name="foo/foofoo v1/Testing" tests="5" failures="2" errors="0" id="0" skipped="2" time="">
		<testcase name="test-warning-two-comments" classname="foo/foofoo v1/Testing">
			<failure message="(a) summary: description"></failure>
		</testcase>
		<testcase name="test-warning-two-comments" classname="foo/foofoo v1/Testing">
			<failure message="summary: description"></failure>
		</testcase>
		<testcase name="test-ok-comment" classname="foo/foofoo v1/Testing"></testcase>
		<testcase name="test-skipped-comment" classname="foo/foofoo v1/Testing">
			<skipped message="(a) skipped sum: skipped description"></skipped>
		</testcase>
		<testcase name="test-skipped-no-comment" classname="foo/foofoo v1/Testing">
			<skipped message=""></skipped>
		</testcase>
	</testsuite>
	<testsuite name="bar-no-namespace v1/Testing" tests="5" failures="2" errors="0" id="0" skipped="2" time="">
		<testcase name="test-warning-two-comments" classname="bar-no-namespace v1/Testing">
			<failure message="(a) summary: description"></failure>
		</testcase>
		<testcase name="test-warning-two-comments" classname="bar-no-namespace v1/Testing">
			<failure message="summary: description"></failure>
		</testcase>
		<testcase name="test-ok-comment" classname="bar-no-namespace v1/Testing"></testcase>
		<testcase name="test-skipped-comment" classname="bar-no-namespace v1/Testing">
			<skipped message="(a) skipped sum: skipped description"></skipped>
		</testcase>
		<testcase name="test-skipped-no-comment" classname="bar-no-namespace v1/Testing">
			<skipped message=""></skipped>
		</testcase>
	</testsuite>
</testsuites>
`, string(all))
}
0707010000004F000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002100000000kube-score-1.20.0/renderer/sarif07070100000050000081A4000000000000000000000001680FB1F8000007CE000000000000000000000000000000000000002A00000000kube-score-1.20.0/renderer/sarif/sarif.gopackage sarif

import (
	"bytes"
	"encoding/json"
	"io"

	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/sarif"
	"github.com/zegl/kube-score/scorecard"
)

func Output(input *scorecard.Scorecard) io.Reader {
	var results []sarif.Results
	var rules []sarif.Rules

	addRule := func(check domain.Check) {
		for _, r := range rules {
			if r.ID == check.ID {
				return
			}
		}

		rules = append(rules, sarif.Rules{
			ID:   check.ID,
			Name: check.Name,
		})
	}

	for _, v := range *input {
		for _, check := range v.Checks {
			if check.Skipped {
				continue
			}

			var level string
			switch check.Grade {
			case scorecard.GradeCritical:
				level = "error"
			case scorecard.GradeWarning:
				level = "warning"
			default:
				continue
			}

			addRule(check.Check)

			for _, comment := range check.Comments {
				results = append(results, sarif.Results{
					Message: sarif.Message{
						Text: comment.Summary,
					},
					RuleID: check.Check.ID,
					Level:  level,
					Properties: sarif.ResultsProperties{
						IssueConfidence: "HIGH",
						IssueSeverity:   "HIGH",
					},
					Locations: []sarif.Locations{
						{
							PhysicalLocation: sarif.PhysicalLocation{
								ArtifactLocation: sarif.ArtifactLocation{
									URI: "file://" + v.FileLocation.Name,
								},
								ContextRegion: sarif.ContextRegion{
									StartLine: v.FileLocation.Line,
								},
							},
						},
					},
				})
			}
		}
	}

	run := sarif.Run{
		Tool: sarif.Tool{
			Driver: sarif.Driver{
				Name:  "kube-score",
				Rules: rules,
			},
		},
		Conversion: sarif.Conversion{
			Tool: sarif.Tool{
				Driver: sarif.Driver{
					Name: "kube-score"},
			},
		},
		Results: results,
	}
	res := sarif.Sarif{
		Runs:    []sarif.Run{run},
		Version: "2.1.0",
		Schema:  "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
	}

	j, err := json.MarshalIndent(res, "", "    ")
	if err != nil {
		panic(err)
	}
	return bytes.NewBuffer(j)
}
07070100000051000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001800000000kube-score-1.20.0/sarif07070100000052000081A4000000000000000000000001680FB1F800000B9F000000000000000000000000000000000000002100000000kube-score-1.20.0/sarif/sarif.gopackage sarif

import (
	"time"
)

type Sarif struct {
	Runs    []Run  `json:"runs,omitempty"`
	Version string `json:"version,omitempty"`
	Schema  string `json:"$schema,omitempty"`
}

type Rules struct {
	ID      string `json:"id,omitempty"`
	Name    string `json:"name,omitempty"`
	HelpURI string `json:"helpUri,omitempty"`
}

type Driver struct {
	Name  string  `json:"name,omitempty"`
	Rules []Rules `json:"rules,omitempty"`
}

type Tool struct {
	Driver Driver `json:"driver,omitempty"`
}

type WorkingDirectory struct {
	URI string `json:"uri,omitempty"`
}

type Invocation struct {
	Arguments           []string         `json:"arguments,omitempty"`
	ExecutionSuccessful bool             `json:"executionSuccessful"`
	CommandLine         string           `json:"commandLine,omitempty"`
	EndTimeUtc          time.Time        `json:"endTimeUtc,omitempty"`
	WorkingDirectory    WorkingDirectory `json:"workingDirectory,omitempty"`
}

type Conversion struct {
	Tool       Tool       `json:"tool,omitempty"`
	Invocation Invocation `json:"invocation,omitempty"`
}

type Invocations struct {
	ExecutionSuccessful bool             `json:"executionSuccessful,omitempty"`
	EndTimeUtc          time.Time        `json:"endTimeUtc,omitempty"`
	WorkingDirectory    WorkingDirectory `json:"workingDirectory,omitempty"`
}

type Properties struct {
}

type Message struct {
	Text string `json:"text,omitempty"`
}

type Snippet struct {
	Text string `json:"text,omitempty"`
}

type Region struct {
	Snippet   Snippet `json:"snippet,omitempty"`
	StartLine int     `json:"startLine,omitempty"`
}

type ArtifactLocation struct {
	URI string `json:"uri,omitempty"`
}

type ContextRegion struct {
	Snippet   Snippet `json:"snippet,omitempty"`
	EndLine   int     `json:"endLine,omitempty"`
	StartLine int     `json:"startLine,omitempty"`
}

type PhysicalLocation struct {
	Region           Region           `json:"region,omitempty"`
	ArtifactLocation ArtifactLocation `json:"artifactLocation,omitempty"`
	ContextRegion    ContextRegion    `json:"contextRegion,omitempty"`
}

type Locations struct {
	PhysicalLocation PhysicalLocation `json:"physicalLocation,omitempty"`
}

type ResultsProperties struct {
	IssueConfidence string `json:"issue_confidence,omitempty"`
	IssueSeverity   string `json:"issue_severity,omitempty"`
}

type Results struct {
	Message    Message           `json:"message,omitempty"`
	Level      string            `json:"level,omitempty"`
	Locations  []Locations       `json:"locations,omitempty"`
	Properties ResultsProperties `json:"properties,omitempty"`
	RuleID     string            `json:"ruleId,omitempty"`
	RuleIndex  int               `json:"ruleIndex,omitempty"`
}

type Run struct {
	Tool        Tool          `json:"tool,omitempty"`
	Conversion  Conversion    `json:"conversion,omitempty"`
	Invocations []Invocations `json:"invocations,omitempty"`
	Properties  Properties    `json:"properties,omitempty"`
	Results     []Results     `json:"results,omitempty"`
}
07070100000053000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001800000000kube-score-1.20.0/score07070100000054000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001D00000000kube-score-1.20.0/score/apps07070100000055000081A4000000000000000000000001680FB1F80000237A000000000000000000000000000000000000002500000000kube-score-1.20.0/score/apps/apps.gopackage apps

import (
	"fmt"
	"strings"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/score/internal"
	"github.com/zegl/kube-score/scorecard"
)

func Register(allChecks *checks.Checks, allHPAs []ks.HpaTargeter, allServices []ks.Service) {
	allChecks.RegisterDeploymentCheck("Deployment has host PodAntiAffinity", "Makes sure that a podAntiAffinity has been set that prevents multiple pods from being scheduled on the same node. https://kubernetes.io/docs/concepts/configuration/assign-pod-node/", deploymentHasAntiAffinity)
	allChecks.RegisterStatefulSetCheck("StatefulSet has host PodAntiAffinity", "Makes sure that a podAntiAffinity has been set that prevents multiple pods from being scheduled on the same node. https://kubernetes.io/docs/concepts/configuration/assign-pod-node/", statefulsetHasAntiAffinity)

	allChecks.RegisterDeploymentCheck("Deployment targeted by HPA does not have replicas configured", "Makes sure that Deployments using a HorizontalPodAutoscaler doesn't have a statically configured replica count set", hpaDeploymentNoReplicas(allHPAs))
	allChecks.RegisterStatefulSetCheck("StatefulSet has ServiceName", "Makes sure that StatefulSets have an existing headless serviceName.", statefulsetHasServiceName(allServices))

	allChecks.RegisterDeploymentCheck("Deployment Pod Selector labels match template metadata labels", "Ensure the StatefulSet selector labels match the template metadata labels.", deploymentSelectorLabelsMatching)
	allChecks.RegisterStatefulSetCheck("StatefulSet Pod Selector labels match template metadata labels", "Ensure the StatefulSet selector labels match the template metadata labels.", statefulSetSelectorLabelsMatching)
}

func hpaDeploymentNoReplicas(allHPAs []ks.HpaTargeter) func(deployment appsv1.Deployment) (scorecard.TestScore, error) {
	return func(deployment appsv1.Deployment) (score scorecard.TestScore, err error) {
		// If is targeted by a HPA
		for _, hpa := range allHPAs {
			target := hpa.HpaTarget()

			if hpa.GetObjectMeta().Namespace == deployment.Namespace &&
				strings.EqualFold(target.Kind, deployment.Kind) &&
				target.Name == deployment.Name {

				if deployment.Spec.Replicas == nil {
					score.Grade = scorecard.GradeAllOK
					return
				}

				score.Grade = scorecard.GradeCritical
				score.AddComment("", "The deployment is targeted by a HPA, but a static replica count is configured in the DeploymentSpec", "When replicas are both statically set and managed by the HPA, the replicas will be changed to the statically configured count when the spec is applied, even if the HPA wants the replica count to be higher.")
				return
			}
		}

		score.Grade = scorecard.GradeAllOK
		score.Skipped = true
		score.AddComment("", "Skipped because the deployment is not targeted by a HorizontalPodAutoscaler", "")
		return
	}
}

func deploymentHasAntiAffinity(deployment appsv1.Deployment) (score scorecard.TestScore, err error) {
	// Ignore if the deployment only has a single replica
	// If replicas is not explicitly set, we'll still warn if the anti affinity is missing
	// as that might indicate use of a Horizontal Pod Autoscaler
	if deployment.Spec.Replicas != nil && *deployment.Spec.Replicas < 2 {
		score.Skipped = true
		score.AddComment("", "Skipped because the deployment has less than 2 replicas", "")
		return
	}

	warn := func() {
		score.Grade = scorecard.GradeWarning
		score.AddComment("", "Deployment does not have a host podAntiAffinity set", "It's recommended to set a podAntiAffinity that stops multiple pods from a deployment from being scheduled on the same node. This increases availability in case the node becomes unavailable.")
	}

	affinity := deployment.Spec.Template.Spec.Affinity
	if affinity == nil || affinity.PodAntiAffinity == nil {
		warn()
		return
	}

	labels := internal.MapLabels(deployment.Spec.Template.GetObjectMeta().GetLabels())

	if hasPodAntiAffinity(labels, affinity) {
		score.Grade = scorecard.GradeAllOK
		return
	}

	warn()
	return
}

func statefulsetHasAntiAffinity(statefulset appsv1.StatefulSet) (score scorecard.TestScore, err error) {
	// Ignore if the statefulset only has a single replica
	// If replicas is not explicitly set, we'll still warn if the anti affinity is missing
	// as that might indicate use of a Horizontal Pod Autoscaler
	if statefulset.Spec.Replicas != nil && *statefulset.Spec.Replicas < 2 {
		score.Skipped = true
		score.AddComment("", "Skipped because the statefulset has less than 2 replicas", "")
		return
	}

	warn := func() {
		score.Grade = scorecard.GradeWarning
		score.AddComment("", "StatefulSet does not have a host podAntiAffinity set", "It's recommended to set a podAntiAffinity that stops multiple pods from a statefulset from being scheduled on the same node. This increases availability in case the node becomes unavailable.")
	}

	affinity := statefulset.Spec.Template.Spec.Affinity
	if affinity == nil || affinity.PodAntiAffinity == nil {
		warn()
		return
	}

	labels := internal.MapLabels(statefulset.Spec.Template.GetObjectMeta().GetLabels())

	if hasPodAntiAffinity(labels, affinity) {
		score.Grade = scorecard.GradeAllOK
		return
	}

	warn()
	return
}

func hasPodAntiAffinity(selfLabels internal.MapLabels, affinity *corev1.Affinity) bool {
	approvedTopologyKeys := map[string]struct{}{
		"kubernetes.io/hostname":        {},
		"topology.kubernetes.io/region": {},
		"topology.kubernetes.io/zone":   {},

		// Deprecated in Kubernetes v1.17
		"failure-domain.beta.kubernetes.io/region": {},
		"failure-domain.beta.kubernetes.io/zone":   {},
	}

	for _, pref := range affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
		if _, ok := approvedTopologyKeys[pref.PodAffinityTerm.TopologyKey]; ok {
			if selector, err := metav1.LabelSelectorAsSelector(pref.PodAffinityTerm.LabelSelector); err == nil {
				if selector.Matches(selfLabels) {
					return true
				}
			}
		}
	}

	for _, req := range affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution {
		if _, ok := approvedTopologyKeys[req.TopologyKey]; ok {
			if selector, err := metav1.LabelSelectorAsSelector(req.LabelSelector); err == nil {
				if selector.Matches(selfLabels) {
					return true
				}
			}
		}
	}

	return false
}

func statefulsetHasServiceName(allServices []ks.Service) func(statefulset appsv1.StatefulSet) (scorecard.TestScore, error) {
	return func(statefulset appsv1.StatefulSet) (score scorecard.TestScore, err error) {
		for _, service := range allServices {
			if service.Service().Namespace != statefulset.Namespace ||
				service.Service().Name != statefulset.Spec.ServiceName ||
				service.Service().Spec.ClusterIP != "None" {
				continue
			}

			if internal.LabelSelectorMatchesLabels(
				service.Service().Spec.Selector,
				statefulset.Spec.Template.GetObjectMeta().GetLabels(),
			) {
				score.Grade = scorecard.GradeAllOK
				return
			}
		}

		score.Grade = scorecard.GradeCritical
		score.AddComment("", "StatefulSet does not have a valid serviceName", "StatefulSets currently require a Headless Service to be responsible for the network identity of the Pods. You are responsible for creating this Service. https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#limitations")
		return
	}
}

func statefulSetSelectorLabelsMatching(statefulset appsv1.StatefulSet) (score scorecard.TestScore, err error) {
	selector, err := metav1.LabelSelectorAsSelector(statefulset.Spec.Selector)
	if err != nil {
		score.Grade = scorecard.GradeCritical
		score.AddComment("", "StatefulSet selector labels are not matching template metadata labels", fmt.Sprintf("Invalid selector: %s", err))
		return
	}

	if selector.Matches(internal.MapLabels(statefulset.Spec.Template.GetObjectMeta().GetLabels())) {
		score.Grade = scorecard.GradeAllOK
		return
	}

	score.Grade = scorecard.GradeCritical
	score.AddComment("", "StatefulSet selector labels not matching template metadata labels", "StatefulSets require `.spec.selector` to match `.spec.template.metadata.labels`. https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-selector")
	return
}

func deploymentSelectorLabelsMatching(deployment appsv1.Deployment) (score scorecard.TestScore, err error) {
	selector, err := metav1.LabelSelectorAsSelector(deployment.Spec.Selector)
	if err != nil {
		score.Grade = scorecard.GradeCritical
		score.AddComment("", "Deployment selector labels are not matching template metadata labels", fmt.Sprintf("Invalid selector: %s", err))
		return
	}

	if selector.Matches(internal.MapLabels(deployment.Spec.Template.GetObjectMeta().GetLabels())) {
		score.Grade = scorecard.GradeAllOK
		return
	}

	score.Grade = scorecard.GradeCritical
	score.AddComment("", "Deployment selector labels not matching template metadata labels", "Deployment require `.spec.selector` to match `.spec.template.metadata.labels`. https://kubernetes.io/docs/concepts/workloads/controllers/deployment/")
	return
}
07070100000056000081A4000000000000000000000001680FB1F80000595F000000000000000000000000000000000000002A00000000kube-score-1.20.0/score/apps/apps_test.gopackage apps

import (
	"testing"

	"github.com/stretchr/testify/assert"
	appsv1 "k8s.io/api/apps/v1"
	autoscalingv1 "k8s.io/api/autoscaling/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
)

type testcase struct {
	replicas        *int32
	affinity        *corev1.Affinity
	expectedGrade   scorecard.Grade
	expectedSkipped bool
}

func i(i int32) *int32 {
	return &i
}

func antiAffinityTestCases() []testcase {
	return []testcase{
		{
			// No affinity configured
			expectedGrade:   scorecard.GradeWarning,
			replicas:        i(5),
			expectedSkipped: false,
		},
		{
			// OK! (required)
			expectedGrade: scorecard.GradeAllOK,
			replicas:      i(5),
			affinity: &corev1.Affinity{
				PodAntiAffinity: &corev1.PodAntiAffinity{
					RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
						{
							TopologyKey: "kubernetes.io/hostname",
							LabelSelector: &metav1.LabelSelector{
								MatchLabels: map[string]string{
									"app": "foo",
								},
							},
						},
					},
				},
			},
			expectedSkipped: false,
		},
		{
			// OK! (required) ( topology.kubernetes.io/zone )
			expectedGrade: scorecard.GradeAllOK,
			replicas:      i(5),
			affinity: &corev1.Affinity{
				PodAntiAffinity: &corev1.PodAntiAffinity{
					RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
						{
							TopologyKey: "topology.kubernetes.io/zone",
							LabelSelector: &metav1.LabelSelector{
								MatchLabels: map[string]string{
									"app": "foo",
								},
							},
						},
					},
				},
			},
			expectedSkipped: false,
		},
		{
			// OK! (required) ( topology.kubernetes.io/region )
			expectedGrade: scorecard.GradeAllOK,
			replicas:      i(5),
			affinity: &corev1.Affinity{
				PodAntiAffinity: &corev1.PodAntiAffinity{
					RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
						{
							TopologyKey: "topology.kubernetes.io/region",
							LabelSelector: &metav1.LabelSelector{
								MatchLabels: map[string]string{
									"app": "foo",
								},
							},
						},
					},
				},
			},
			expectedSkipped: false,
		},
		{
			// Not OK! (required) ( some other topology key )
			expectedGrade: scorecard.GradeWarning,
			replicas:      i(5),
			affinity: &corev1.Affinity{
				PodAntiAffinity: &corev1.PodAntiAffinity{
					RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
						{
							TopologyKey: "topology.kubernetes.io/what-is-this-key",
							LabelSelector: &metav1.LabelSelector{
								MatchLabels: map[string]string{
									"app": "foo",
								},
							},
						},
					},
				},
			},
			expectedSkipped: false,
		},
		{
			// OK (preferred)
			expectedGrade: scorecard.GradeAllOK,
			replicas:      i(5),
			affinity: &corev1.Affinity{
				PodAntiAffinity: &corev1.PodAntiAffinity{
					PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{
						{
							Weight: 100,
							PodAffinityTerm: corev1.PodAffinityTerm{
								TopologyKey: "kubernetes.io/hostname",
								LabelSelector: &metav1.LabelSelector{
									MatchLabels: map[string]string{
										"app": "foo",
									},
								},
							},
						},
					},
				},
			},
			expectedSkipped: false,
		},
		{
			// Not matching app label
			expectedGrade: scorecard.GradeWarning,
			replicas:      i(5),
			affinity: &corev1.Affinity{
				PodAntiAffinity: &corev1.PodAntiAffinity{
					RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
						{
							TopologyKey: "kubernetes.io/hostname",
							LabelSelector: &metav1.LabelSelector{
								MatchLabels: map[string]string{
									"app": "not-foo",
								},
							},
						},
					},
				},
			},
			expectedSkipped: false,
		},
		{
			// Less than two replicas
			expectedGrade:   0,
			replicas:        i(1),
			expectedSkipped: true,
		},
	}
}

func TestStatefulsetHasAntiAffinity(t *testing.T) {
	t.Parallel()
	for caseID, tc := range antiAffinityTestCases() {
		s := appsv1.StatefulSet{
			Spec: appsv1.StatefulSetSpec{
				Replicas: tc.replicas,
				Template: corev1.PodTemplateSpec{
					ObjectMeta: metav1.ObjectMeta{
						Labels: map[string]string{
							"app": "foo",
						},
					},
					Spec: corev1.PodSpec{
						Affinity: tc.affinity,
					},
				},
			},
		}

		score, err := statefulsetHasAntiAffinity(s)
		assert.Nil(t, err)
		assert.Equal(t, tc.expectedGrade, score.Grade, "caseID=%d", caseID)
	}
}

func TestDeploymentHasAntiAffinity(t *testing.T) {
	t.Parallel()
	for caseID, tc := range antiAffinityTestCases() {
		s := appsv1.Deployment{
			Spec: appsv1.DeploymentSpec{
				Replicas: tc.replicas,
				Template: corev1.PodTemplateSpec{
					ObjectMeta: metav1.ObjectMeta{
						Labels: map[string]string{
							"app": "foo",
						},
					},
					Spec: corev1.PodSpec{
						Affinity: tc.affinity,
					},
				},
			},
		}

		score, err := deploymentHasAntiAffinity(s)
		assert.Nil(t, err)
		assert.Equal(t, tc.expectedGrade, score.Grade, "unexpected grade caseID=%d", caseID)
		assert.Equal(t, tc.expectedSkipped, score.Skipped, "unexpected skipped, caseID=%d", caseID)
	}
}

func TestDeploymentTargetedByHpaHasNoReplicasAllOK(t *testing.T) {
	t.Parallel()

	deployment := appsv1.Deployment{
		TypeMeta:   metav1.TypeMeta{Kind: "Deployment"},
		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
		Spec: appsv1.DeploymentSpec{
			Replicas: nil,
		},
	}

	hpas := []ks.HpaTargeter{
		hpav1{
			autoscalingv1.HorizontalPodAutoscaler{
				Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
					ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
						Kind:       "Deployment",
						Name:       "foo",
						APIVersion: "apps/v1",
					},
				},
			},
		},
	}

	f := hpaDeploymentNoReplicas(hpas)
	score, err := f(deployment)
	assert.Nil(t, err)
	assert.Equal(t, scorecard.GradeAllOK, score.Grade)
	assert.False(t, score.Skipped)
}

func TestDeploymentTargetedByHpaHasSetReplicasCritical(t *testing.T) {
	t.Parallel()

	deployment := appsv1.Deployment{
		TypeMeta:   metav1.TypeMeta{Kind: "Deployment"},
		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
		Spec: appsv1.DeploymentSpec{
			Replicas: i(30),
		},
	}

	hpas := []ks.HpaTargeter{
		hpav1{
			autoscalingv1.HorizontalPodAutoscaler{
				Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
					ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
						Kind:       "Deployment",
						Name:       "foo",
						APIVersion: "apps/v1",
					},
				},
			},
		},
	}

	f := hpaDeploymentNoReplicas(hpas)
	score, err := f(deployment)
	assert.Nil(t, err)
	assert.Equal(t, scorecard.GradeCritical, score.Grade)
	assert.False(t, score.Skipped)
}

func TestDeploymentNotTargetedByHpaIsSkippedAllOKK(t *testing.T) {
	t.Parallel()

	deployment := appsv1.Deployment{
		TypeMeta:   metav1.TypeMeta{Kind: "Deployment"},
		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
		Spec: appsv1.DeploymentSpec{
			Replicas: i(30),
		},
	}

	hpas := []ks.HpaTargeter{
		hpav1{
			autoscalingv1.HorizontalPodAutoscaler{
				Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
					ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
						Kind:       "Deployment",
						Name:       "some-other-foo",
						APIVersion: "apps/v1",
					},
				},
			},
		},
	}

	f := hpaDeploymentNoReplicas(hpas)
	score, err := f(deployment)
	assert.Nil(t, err)
	assert.Equal(t, scorecard.GradeAllOK, score.Grade)
	assert.True(t, score.Skipped)
}

func TestDeploymentTargetedByHpaHasNoReplicasAllOKCaseInsensitiveKind(t *testing.T) {
	t.Parallel()

	deployment := appsv1.Deployment{
		TypeMeta:   metav1.TypeMeta{Kind: "Deployment"},
		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
		Spec: appsv1.DeploymentSpec{
			Replicas: nil,
		},
	}

	hpas := []ks.HpaTargeter{
		hpav1{
			autoscalingv1.HorizontalPodAutoscaler{
				Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
					ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
						Kind:       "deployment",
						Name:       "foo",
						APIVersion: "apps/v1",
					},
				},
			},
		},
	}

	f := hpaDeploymentNoReplicas(hpas)
	score, err := f(deployment)
	assert.Nil(t, err)
	assert.Equal(t, scorecard.GradeAllOK, score.Grade)
	assert.False(t, score.Skipped)
}

type hpav1 struct {
	autoscalingv1.HorizontalPodAutoscaler
}

func (d hpav1) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d hpav1) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d hpav1) MinReplicas() *int32 {
	return d.Spec.MinReplicas
}

func (d hpav1) HpaTarget() autoscalingv1.CrossVersionObjectReference {
	return d.Spec.ScaleTargetRef
}

func (hpav1) FileLocation() ks.FileLocation {
	return ks.FileLocation{}
}

func TestStatefulSetHasServiceName(t *testing.T) {
	t.Parallel()

	testcases := []struct {
		statefulset     appsv1.StatefulSet
		services        []ks.Service
		expectedErr     error
		expectedGrade   scorecard.Grade
		expectedSkipped bool
	}{
		// Match (no namespace)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.StatefulSetSpec{
					ServiceName: "foo-svc",
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			services: []ks.Service{
				service{
					corev1.Service{
						ObjectMeta: metav1.ObjectMeta{Name: "foo-svc"},
						Spec: corev1.ServiceSpec{
							ClusterIP: "None",
							Selector: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			expectedErr:     nil,
			expectedGrade:   scorecard.GradeAllOK,
			expectedSkipped: false,
		},

		// No match (different service name)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.StatefulSetSpec{
					ServiceName: "bar-svc",
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			services: []ks.Service{
				service{
					corev1.Service{
						ObjectMeta: metav1.ObjectMeta{Name: "foo-svc"},
						Spec: corev1.ServiceSpec{
							ClusterIP: "None",
							Selector: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			expectedErr:     nil,
			expectedGrade:   scorecard.GradeCritical,
			expectedSkipped: false,
		},

		// No match (missing service name)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.StatefulSetSpec{
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			services: []ks.Service{
				service{
					corev1.Service{
						ObjectMeta: metav1.ObjectMeta{Name: "foo-svc"},
						Spec: corev1.ServiceSpec{
							ClusterIP: "None",
							Selector: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			expectedErr:     nil,
			expectedGrade:   scorecard.GradeCritical,
			expectedSkipped: false,
		},

		// Match (same namespace)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foo-ns"},
				Spec: appsv1.StatefulSetSpec{
					ServiceName: "foo-svc",
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			services: []ks.Service{
				service{
					corev1.Service{
						ObjectMeta: metav1.ObjectMeta{Name: "foo-svc", Namespace: "foo-ns"},
						Spec: corev1.ServiceSpec{
							ClusterIP: "None",
							Selector: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			expectedErr:     nil,
			expectedGrade:   scorecard.GradeAllOK,
			expectedSkipped: false,
		},

		// No match (different namespace)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foo-ns"},
				Spec: appsv1.StatefulSetSpec{
					ServiceName: "foo-svc",
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			services: []ks.Service{
				service{
					corev1.Service{
						ObjectMeta: metav1.ObjectMeta{Name: "foo-svc", Namespace: "bar-ns"},
						Spec: corev1.ServiceSpec{
							ClusterIP: "None",
							Selector: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			expectedErr:     nil,
			expectedGrade:   scorecard.GradeCritical,
			expectedSkipped: false,
		},

		// Match (multiple namespaces)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foo-ns"},
				Spec: appsv1.StatefulSetSpec{
					ServiceName: "foo-svc",
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			services: []ks.Service{
				service{
					corev1.Service{
						ObjectMeta: metav1.ObjectMeta{Name: "foo-svc", Namespace: "bar-ns"},
						Spec: corev1.ServiceSpec{
							ClusterIP: "None",
							Selector: map[string]string{
								"app": "foo",
							},
						},
					},
				},
				service{
					corev1.Service{
						ObjectMeta: metav1.ObjectMeta{Name: "foo-svc", Namespace: "foo-ns"},
						Spec: corev1.ServiceSpec{
							ClusterIP: "None",
							Selector: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			expectedErr:     nil,
			expectedGrade:   scorecard.GradeAllOK,
			expectedSkipped: false,
		},

		// Match (multiple namespaces, reversed)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foo-ns"},
				Spec: appsv1.StatefulSetSpec{
					ServiceName: "foo-svc",
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			services: []ks.Service{
				service{
					corev1.Service{
						ObjectMeta: metav1.ObjectMeta{Name: "foo-svc", Namespace: "foo-ns"},
						Spec: corev1.ServiceSpec{
							ClusterIP: "None",
							Selector: map[string]string{
								"app": "foo",
							},
						},
					},
				},
				service{
					corev1.Service{
						ObjectMeta: metav1.ObjectMeta{Name: "foo-svc", Namespace: "bar-ns"},
						Spec: corev1.ServiceSpec{
							ClusterIP: "None",
							Selector: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			expectedErr:     nil,
			expectedGrade:   scorecard.GradeAllOK,
			expectedSkipped: false,
		},

		// No match (not headless service)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.StatefulSetSpec{
					ServiceName: "foo-svc",
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			services: []ks.Service{
				service{
					corev1.Service{
						ObjectMeta: metav1.ObjectMeta{Name: "foo-svc"},
						Spec: corev1.ServiceSpec{
							ClusterIP: "",
							Selector: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			expectedErr:     nil,
			expectedGrade:   scorecard.GradeCritical,
			expectedSkipped: false,
		},

		// No match (selector)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.StatefulSetSpec{
					ServiceName: "foo-svc",
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			services: []ks.Service{
				service{
					corev1.Service{
						ObjectMeta: metav1.ObjectMeta{Name: "foo-svc"},
						Spec: corev1.ServiceSpec{
							ClusterIP: "None",
							Selector: map[string]string{
								"app": "bar",
							},
						},
					},
				},
			},
			expectedErr:     nil,
			expectedGrade:   scorecard.GradeCritical,
			expectedSkipped: false,
		},
	}

	for _, tc := range testcases {
		fn := statefulsetHasServiceName(tc.services)
		score, err := fn(tc.statefulset)
		assert.Equal(t, tc.expectedErr, err)
		assert.Equal(t, tc.expectedGrade, score.Grade)
		assert.Equal(t, tc.expectedSkipped, score.Skipped)
	}
}

func TestStatefulSetSelectorLabels(t *testing.T) {
	t.Parallel()

	testcases := []struct {
		statefulset   appsv1.StatefulSet
		expectedErr   error
		expectedGrade scorecard.Grade
	}{
		// Match
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.StatefulSetSpec{
					Selector: &metav1.LabelSelector{
						MatchLabels: map[string]string{
							"app": "foo",
						},
					},
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			expectedErr:   nil,
			expectedGrade: scorecard.GradeAllOK,
		},

		// No match (labels differ)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.StatefulSetSpec{
					Selector: &metav1.LabelSelector{
						MatchLabels: map[string]string{
							"app": "foo",
						},
					},
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "bar",
							},
						},
					},
				},
			},
			expectedErr:   nil,
			expectedGrade: scorecard.GradeCritical,
		},

		// Match (expression)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.StatefulSetSpec{
					Selector: &metav1.LabelSelector{
						MatchExpressions: []metav1.LabelSelectorRequirement{
							{
								Key:      "app",
								Operator: metav1.LabelSelectorOpIn,
								Values:   []string{"aaa", "bbb", "bar"},
							},
						},
					},
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "bar",
							},
						},
					},
				},
			},
			expectedErr:   nil,
			expectedGrade: scorecard.GradeAllOK,
		},

		// No match (expression)
		{
			statefulset: appsv1.StatefulSet{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.StatefulSetSpec{
					Selector: &metav1.LabelSelector{
						MatchExpressions: []metav1.LabelSelectorRequirement{
							{
								Key:      "app",
								Operator: metav1.LabelSelectorOpNotIn,
								Values:   []string{"aaa", "bbb", "bar"},
							},
						},
					},
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "bar",
							},
						},
					},
				},
			},
			expectedErr:   nil,
			expectedGrade: scorecard.GradeCritical,
		},
	}

	for _, tc := range testcases {
		score, err := statefulSetSelectorLabelsMatching(tc.statefulset)
		assert.Equal(t, tc.expectedErr, err)
		assert.Equal(t, tc.expectedGrade, score.Grade)
	}
}

func TestDeploymentSelectorLabels(t *testing.T) {
	t.Parallel()

	testcases := []struct {
		statefulset   appsv1.Deployment
		expectedErr   error
		expectedGrade scorecard.Grade
	}{
		// Match
		{
			statefulset: appsv1.Deployment{
				TypeMeta:   metav1.TypeMeta{Kind: "Deployment"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.DeploymentSpec{
					Selector: &metav1.LabelSelector{
						MatchLabels: map[string]string{
							"app": "foo",
						},
					},
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "foo",
							},
						},
					},
				},
			},
			expectedErr:   nil,
			expectedGrade: scorecard.GradeAllOK,
		},

		// No match (labels differ)
		{
			statefulset: appsv1.Deployment{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.DeploymentSpec{
					Selector: &metav1.LabelSelector{
						MatchLabels: map[string]string{
							"app": "foo",
						},
					},
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "bar",
							},
						},
					},
				},
			},
			expectedErr:   nil,
			expectedGrade: scorecard.GradeCritical,
		},

		// Match (expression)
		{
			statefulset: appsv1.Deployment{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.DeploymentSpec{
					Selector: &metav1.LabelSelector{
						MatchExpressions: []metav1.LabelSelectorRequirement{
							{
								Key:      "app",
								Operator: metav1.LabelSelectorOpIn,
								Values:   []string{"aaa", "bbb", "bar"},
							},
						},
					},
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "bar",
							},
						},
					},
				},
			},
			expectedErr:   nil,
			expectedGrade: scorecard.GradeAllOK,
		},

		// No match (expression)
		{
			statefulset: appsv1.Deployment{
				TypeMeta:   metav1.TypeMeta{Kind: "StatefulSet"},
				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				Spec: appsv1.DeploymentSpec{
					Selector: &metav1.LabelSelector{
						MatchExpressions: []metav1.LabelSelectorRequirement{
							{
								Key:      "app",
								Operator: metav1.LabelSelectorOpNotIn,
								Values:   []string{"aaa", "bbb", "bar"},
							},
						},
					},
					Template: corev1.PodTemplateSpec{
						ObjectMeta: metav1.ObjectMeta{
							Labels: map[string]string{
								"app": "bar",
							},
						},
					},
				},
			},
			expectedErr:   nil,
			expectedGrade: scorecard.GradeCritical,
		},
	}

	for _, tc := range testcases {
		score, err := deploymentSelectorLabelsMatching(tc.statefulset)
		assert.Equal(t, tc.expectedErr, err)
		assert.Equal(t, tc.expectedGrade, score.Grade)
	}
}

type service struct {
	svc corev1.Service
}

func (d service) Service() corev1.Service {
	return d.svc
}

func (d service) FileLocation() ks.FileLocation {
	return ks.FileLocation{}
}
07070100000057000081A4000000000000000000000001680FB1F800001337000000000000000000000000000000000000002500000000kube-score-1.20.0/score/apps_test.gopackage score

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/zegl/kube-score/config"
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
)

func TestDeploymentHasPodAntiAffinityPreffered(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-host-antiaffinity-preffered.yaml", "Deployment has host PodAntiAffinity", scorecard.GradeAllOK)
}

func TestDeploymentHasPodAntiAffinityPrefferedNoSelectorMatch(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-host-antiaffinity-preffered-selector-no-match.yaml", "Deployment has host PodAntiAffinity", scorecard.GradeWarning)
}

func TestDeploymentHasPodAntiAffinityPrefferedSelectorExpression(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-host-antiaffinity-preffered-selector-expression.yaml", "Deployment has host PodAntiAffinity", scorecard.GradeAllOK)
}

func TestDeploymentHasPodAntiAffinityRequired(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-host-antiaffinity-required.yaml", "Deployment has host PodAntiAffinity", scorecard.GradeAllOK)
}

func TestDeploymentHasPodAntiAffinityNotSet(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-host-antiaffinity-not-set.yaml", "Deployment has host PodAntiAffinity", scorecard.GradeWarning)
}

func TestDeploymentHasPodAntiAffinityOneReplica(t *testing.T) {
	t.Parallel()
	// skipped
	testExpectedScore(t, "deployment-host-antiaffinity-1-replica.yaml", "Deployment has host PodAntiAffinity", 0)
}

func TestStatefulSetHasPodAntiAffinityPreffered(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-host-antiaffinity-preffered.yaml", "StatefulSet has host PodAntiAffinity", scorecard.GradeAllOK)
}

func TestStatefulSetHasPodAntiAffinityRequired(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-host-antiaffinity-required.yaml", "StatefulSet has host PodAntiAffinity", scorecard.GradeAllOK)
}

func TestStatefulSetHasPodAntiAffinityNotSet(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-host-antiaffinity-not-set.yaml", "StatefulSet has host PodAntiAffinity", scorecard.GradeWarning)
}

func TestStatefulSetHasPodAntiAffinityOneReplica(t *testing.T) {
	t.Parallel()
	// skipped
	testExpectedScore(t, "statefulset-host-antiaffinity-1-replica.yaml", "StatefulSet has host PodAntiAffinity", 0)
}

func TestStatefulSetHasPodAntiAffinityUndefinedReplicas(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-host-antiaffinity-undefined-replicas.yaml", "StatefulSet has host PodAntiAffinity", scorecard.GradeWarning)
}

func TestDeploymentWithHPAHasReplicas(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-with-hpa-has-replicas.yaml", "Deployment targeted by HPA does not have replicas configured", scorecard.GradeCritical)
}

func TestDeploymentWithHPANotHasReplicas(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-with-hpa-not-has-replicas.yaml", "Deployment targeted by HPA does not have replicas configured", scorecard.GradeAllOK)
}

func TestStatefulsetHasServiceName(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-service-name.yaml", "StatefulSet has ServiceName", scorecard.GradeAllOK)
}

func TestStatefulsetHasServiceNameDifferentName(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-service-name-different-name.yaml", "StatefulSet has ServiceName", scorecard.GradeCritical)
}

func TestStatefulsetHasServiceNameDifferentNamespace(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-service-name-not-headless.yaml", "StatefulSet has ServiceName", scorecard.GradeCritical)
}

func TestStatefulsetHasServiceNameNotHeadless(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-service-name-not-headless.yaml", "StatefulSet has ServiceName", scorecard.GradeCritical)
}

func TestStatefulsetHasServiceNameDifferentLabel(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-service-name-different-label.yaml", "StatefulSet has ServiceName", scorecard.GradeCritical)
}

func TestStatefulsetSelectorLabels(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-different-labels.yaml", "StatefulSet Pod Selector labels match template metadata labels", scorecard.GradeCritical)
}

func TestStatefulsetTemplateIgnores(t *testing.T) {
	t.Parallel()
	skipped := wasSkipped(t, []ks.NamedReader{testFile("statefulset-nested-ignores.yaml")}, nil, &config.RunConfiguration{
		UseIgnoreChecksAnnotation:   true,
		UseOptionalChecksAnnotation: true,
	}, "Container Image Tag")
	assert.True(t, skipped)
}

func TestStatefulsetTemplateIgnoresNotIgnoredWhenFlagDisabled(t *testing.T) {
	t.Parallel()
	skipped := wasSkipped(t, []ks.NamedReader{testFile("statefulset-nested-ignores.yaml")}, nil, &config.RunConfiguration{
		UseIgnoreChecksAnnotation:   false,
		UseOptionalChecksAnnotation: true,
	}, "Container Image Tag")
	assert.False(t, skipped)
}
07070100000058000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001F00000000kube-score-1.20.0/score/checks07070100000059000081A4000000000000000000000001680FB1F800001B8E000000000000000000000000000000000000002900000000kube-score-1.20.0/score/checks/checks.gopackage checks

import (
	"strings"

	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	networkingv1 "k8s.io/api/networking/v1"
)

type Config struct {
	IgnoredTests map[string]struct{}
}

func New(cnf *Config) *Checks {
	if cnf == nil {
		cnf = &Config{}
	}
	return &Checks{
		cnf: cnf,

		all:                      make([]ks.Check, 0),
		metas:                    make(map[string]GenCheck[ks.BothMeta]),
		pods:                     make(map[string]GenCheck[ks.PodSpecer]),
		services:                 make(map[string]GenCheck[corev1.Service]),
		statefulsets:             make(map[string]GenCheck[appsv1.StatefulSet]),
		deployments:              make(map[string]GenCheck[appsv1.Deployment]),
		networkpolicies:          make(map[string]GenCheck[networkingv1.NetworkPolicy]),
		ingresses:                make(map[string]GenCheck[ks.Ingress]),
		cronjobs:                 make(map[string]GenCheck[ks.CronJob]),
		horizontalPodAutoscalers: make(map[string]GenCheck[ks.HpaTargeter]),
		poddisruptionbudgets:     make(map[string]GenCheck[ks.PodDisruptionBudget]),
	}
}

func NewCheck(name, targetType, comment string, optional bool) ks.Check {
	return ks.Check{
		Name:       name,
		ID:         machineFriendlyName(name),
		TargetType: targetType,
		Comment:    comment,
		Optional:   optional,
	}
}

func machineFriendlyName(in string) string {
	in = strings.ToLower(in)
	in = strings.ReplaceAll(in, " ", "-")
	return in
}

type MetaCheckFn = func(ks.BothMeta) scorecard.TestScore
type MetaCheck struct {
	ks.Check
	Fn MetaCheckFn
}

type CheckFunc[T any] func(T) (scorecard.TestScore, error)

type GenCheck[T any] struct {
	ks.Check
	Fn CheckFunc[T]
}

type Checks struct {
	all                      []ks.Check
	metas                    map[string]GenCheck[ks.BothMeta]
	pods                     map[string]GenCheck[ks.PodSpecer]
	services                 map[string]GenCheck[corev1.Service]
	statefulsets             map[string]GenCheck[appsv1.StatefulSet]
	deployments              map[string]GenCheck[appsv1.Deployment]
	networkpolicies          map[string]GenCheck[networkingv1.NetworkPolicy]
	ingresses                map[string]GenCheck[ks.Ingress]
	cronjobs                 map[string]GenCheck[ks.CronJob]
	horizontalPodAutoscalers map[string]GenCheck[ks.HpaTargeter]
	poddisruptionbudgets     map[string]GenCheck[ks.PodDisruptionBudget]

	cnf *Config
}

func (c Checks) isEnabled(check ks.Check) bool {
	_, ok := c.cnf.IgnoredTests[check.ID]
	return !ok
}

func (c *Checks) RegisterMetaCheck(name, comment string, fn CheckFunc[ks.BothMeta]) {
	reg(c, "all", name, comment, false, fn, c.metas)
}

func (c *Checks) RegisterOptionalMetaCheck(name, comment string, fn CheckFunc[ks.BothMeta]) {
	reg(c, "all", name, comment, true, fn, c.metas)
}

func (c *Checks) Metas() map[string]GenCheck[ks.BothMeta] {
	return c.metas
}

func reg[T any](c *Checks, targetType, name, comment string, optional bool, fn CheckFunc[T], mp map[string]GenCheck[T]) {
	ch := NewCheck(name, targetType, comment, optional)
	check := GenCheck[T]{Check: ch, Fn: fn}
	c.all = append(c.all, check.Check)
	if !c.isEnabled(check.Check) {
		return
	}
	mp[machineFriendlyName(ch.Name)] = check
}

func (c *Checks) RegisterPodCheck(name, comment string, fn CheckFunc[ks.PodSpecer]) {
	reg(c, "Pod", name, comment, false, fn, c.pods)
}

func (c *Checks) RegisterOptionalPodCheck(name, comment string, fn CheckFunc[ks.PodSpecer]) {
	reg(c, "Pod", name, comment, true, fn, c.pods)
}

func (c *Checks) Pods() map[string]GenCheck[ks.PodSpecer] {
	return c.pods
}

func (c *Checks) RegisterHorizontalPodAutoscalerCheck(name, comment string, fn CheckFunc[ks.HpaTargeter]) {
	reg(c, "HorizontalPodAutoscaler", name, comment, false, fn, c.horizontalPodAutoscalers)
}

func (c *Checks) RegisterOptionalHorizontalPodAutoscalerCheck(name, comment string, fn CheckFunc[ks.HpaTargeter]) {
	reg(c, "HorizontalPodAutoscaler", name, comment, true, fn, c.horizontalPodAutoscalers)
}

func (c *Checks) HorizontalPodAutoscalers() map[string]GenCheck[ks.HpaTargeter] {
	return c.horizontalPodAutoscalers
}

func (c *Checks) RegisterCronJobCheck(name, comment string, fn CheckFunc[ks.CronJob]) {
	reg(c, "CronJob", name, comment, false, fn, c.cronjobs)
}

func (c *Checks) RegisterOptionalCronJobCheck(name, comment string, fn CheckFunc[ks.CronJob]) {
	reg(c, "CronJob", name, comment, true, fn, c.cronjobs)
}

func (c *Checks) CronJobs() map[string]GenCheck[ks.CronJob] {
	return c.cronjobs
}

func (c *Checks) RegisterStatefulSetCheck(name, comment string, fn CheckFunc[appsv1.StatefulSet]) {
	reg(c, "StatefulSet", name, comment, false, fn, c.statefulsets)
}

func (c *Checks) RegisterOptionalStatefulSetCheck(name, comment string, fn CheckFunc[appsv1.StatefulSet]) {
	reg(c, "StatefulSet", name, comment, true, fn, c.statefulsets)
}

func (c *Checks) StatefulSets() map[string]GenCheck[appsv1.StatefulSet] {
	return c.statefulsets
}

func (c *Checks) RegisterDeploymentCheck(name, comment string, fn CheckFunc[appsv1.Deployment]) {
	reg(c, "Deployment", name, comment, false, fn, c.deployments)
}

func (c *Checks) RegisterOptionalDeploymentCheck(name, comment string, fn CheckFunc[appsv1.Deployment]) {
	reg(c, "Deployment", name, comment, true, fn, c.deployments)
}

func (c *Checks) Deployments() map[string]GenCheck[appsv1.Deployment] {
	return c.deployments
}

func (c *Checks) RegisterIngressCheck(name, comment string, fn CheckFunc[ks.Ingress]) {
	reg(c, "Ingress", name, comment, false, fn, c.ingresses)
}

func (c *Checks) RegisterOptionalIngressCheck(name, comment string, fn CheckFunc[ks.Ingress]) {
	reg(c, "Ingress", name, comment, true, fn, c.ingresses)
}

func (c *Checks) Ingresses() map[string]GenCheck[ks.Ingress] {
	return c.ingresses
}

func (c *Checks) RegisterNetworkPolicyCheck(name, comment string, fn CheckFunc[networkingv1.NetworkPolicy]) {
	reg(c, "NetworkPolicy", name, comment, false, fn, c.networkpolicies)
}

func (c *Checks) RegisterOptionalNetworkPolicyCheck(name, comment string, fn CheckFunc[networkingv1.NetworkPolicy]) {
	reg(c, "NetworkPolicy", name, comment, true, fn, c.networkpolicies)
}

func (c *Checks) NetworkPolicies() map[string]GenCheck[networkingv1.NetworkPolicy] {
	return c.networkpolicies
}

func (c *Checks) RegisterPodDisruptionBudgetCheck(name, comment string, fn CheckFunc[ks.PodDisruptionBudget]) {
	reg(c, "PodDisruptionBudget", name, comment, false, fn, c.poddisruptionbudgets)
}

func (c *Checks) PodDisruptionBudgets() map[string]GenCheck[ks.PodDisruptionBudget] {
	return c.poddisruptionbudgets
}

func (c *Checks) RegisterServiceCheck(name, comment string, fn CheckFunc[corev1.Service]) {
	reg(c, "Service", name, comment, false, fn, c.services)
}

func (c *Checks) RegisterOptionalServiceCheck(name, comment string, fn CheckFunc[corev1.Service]) {
	reg(c, "Service", name, comment, true, fn, c.services)
}

func (c *Checks) Services() map[string]GenCheck[corev1.Service] {
	return c.services
}

func (c *Checks) All() []ks.Check {
	return c.all
}
0707010000005A000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002200000000kube-score-1.20.0/score/container0707010000005B000081A4000000000000000000000001680FB1F8000033E8000000000000000000000000000000000000002F00000000kube-score-1.20.0/score/container/container.gopackage container

import (
	"fmt"
	"strings"

	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"
	corev1 "k8s.io/api/core/v1"
)

func Register(allChecks *checks.Checks, ignoreContainerCpuLimitRequirement, ignoreContainerMemoryLimitRequirement bool) {
	allChecks.RegisterPodCheck("Container Resources", `Makes sure that all pods have resource limits and requests set. The --ignore-container-cpu-limit flag can be used to disable the requirement of having a CPU limit`, containerResources(!ignoreContainerCpuLimitRequirement, !ignoreContainerMemoryLimitRequirement))
	allChecks.RegisterOptionalPodCheck("Container Resource Requests Equal Limits", `Makes sure that all pods have the same requests as limits on resources set.`, containerResourceRequestsEqualLimits)
	allChecks.RegisterOptionalPodCheck("Container CPU Requests Equal Limits", `Makes sure that all pods have the same CPU requests as limits set.`, containerCPURequestsEqualLimits)
	allChecks.RegisterOptionalPodCheck("Container Memory Requests Equal Limits", `Makes sure that all pods have the same memory requests as limits set.`, containerMemoryRequestsEqualLimits)
	allChecks.RegisterPodCheck("Container Image Tag", `Makes sure that a explicit non-latest tag is used`, containerImageTag)
	allChecks.RegisterPodCheck("Container Image Pull Policy", `Makes sure that the pullPolicy is set to Always. This makes sure that imagePullSecrets are always validated.`, containerImagePullPolicy)
	allChecks.RegisterPodCheck("Container Ephemeral Storage Request and Limit", "Makes sure all pods have ephemeral-storage requests and limits set", containerStorageEphemeralRequestAndLimit)
	allChecks.RegisterOptionalPodCheck("Container Ephemeral Storage Request Equals Limit", "Make sure all pods have matching ephemeral-storage requests and limits", containerStorageEphemeralRequestEqualsLimit)
	allChecks.RegisterOptionalPodCheck("Container Ports Check", "Container Ports Checks", containerPortsCheck)
	allChecks.RegisterPodCheck("Environment Variable Key Duplication", "Makes sure that duplicated environment variable keys are not duplicated", environmentVariableKeyDuplication)
}

// containerResources makes sure that the container has resource requests and limits set
// The check for a CPU limit requirement can be enabled via the requireCPULimit flag parameter
func containerResources(requireCPULimit bool, requireMemoryLimit bool) func(ks.PodSpecer) (scorecard.TestScore, error) {
	return func(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
		pod := ps.GetPodTemplateSpec().Spec

		allContainers := pod.InitContainers
		allContainers = append(allContainers, pod.Containers...)

		hasMissingLimit := false
		hasMissingRequest := false

		for _, container := range allContainers {
			if container.Resources.Limits.Cpu().IsZero() && requireCPULimit {
				score.AddComment(container.Name, "CPU limit is not set", "Resource limits are recommended to avoid resource DDOS. Set resources.limits.cpu")
				hasMissingLimit = true
			}
			if container.Resources.Limits.Memory().IsZero() && requireMemoryLimit {
				score.AddComment(container.Name, "Memory limit is not set", "Resource limits are recommended to avoid resource DDOS. Set resources.limits.memory")
				hasMissingLimit = true
			}
			if container.Resources.Requests.Cpu().IsZero() {
				score.AddComment(container.Name, "CPU request is not set", "Resource requests are recommended to make sure that the application can start and run without crashing. Set resources.requests.cpu")
				hasMissingRequest = true
			}
			if container.Resources.Requests.Memory().IsZero() {
				score.AddComment(container.Name, "Memory request is not set", "Resource requests are recommended to make sure that the application can start and run without crashing. Set resources.requests.memory")
				hasMissingRequest = true
			}
		}

		switch {
		case len(allContainers) == 0:
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "No containers defined", "")
		case hasMissingLimit:
			score.Grade = scorecard.GradeCritical
		case hasMissingRequest:
			score.Grade = scorecard.GradeWarning
		default:
			score.Grade = scorecard.GradeAllOK
		}

		return
	}
}

// containerResourceRequestsEqualLimits checks that all containers have equal requests and limits for CPU and memory resources
func containerResourceRequestsEqualLimits(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	cpuScore, _ := containerCPURequestsEqualLimits(ps)
	memoryScore, _ := containerMemoryRequestsEqualLimits(ps)

	score.Grade = scorecard.GradeAllOK
	if cpuScore.Grade == scorecard.GradeCritical {
		score.Grade = scorecard.GradeCritical
		score.Comments = append(score.Comments, cpuScore.Comments...)
	}
	if memoryScore.Grade == scorecard.GradeCritical {
		score.Grade = scorecard.GradeCritical
		score.Comments = append(score.Comments, memoryScore.Comments...)
	}

	return
}

// containerCPURequestsEqualLimits checks that all containers have equal requests and limits for CPU resources
func containerCPURequestsEqualLimits(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	pod := ps.GetPodTemplateSpec().Spec

	allContainers := pod.InitContainers
	allContainers = append(allContainers, pod.Containers...)

	resourcesDoNotMatch := false

	for _, container := range allContainers {
		requests := &container.Resources.Requests
		limits := &container.Resources.Limits
		if !requests.Cpu().Equal(*limits.Cpu()) {
			score.AddComment(container.Name, "CPU requests does not match limits", "Having equal requests and limits is recommended to avoid resource DDOS of the node during spikes. Set resources.requests.cpu == resources.limits.cpu")
			resourcesDoNotMatch = true
		}
	}

	if resourcesDoNotMatch {
		score.Grade = scorecard.GradeCritical
	} else {
		score.Grade = scorecard.GradeAllOK
	}

	return
}

// containerMemoryRequestsEqualLimits checks that all containers have equal requests and limits for memory resources
func containerMemoryRequestsEqualLimits(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	pod := ps.GetPodTemplateSpec().Spec

	allContainers := pod.InitContainers
	allContainers = append(allContainers, pod.Containers...)

	resourcesDoNotMatch := false

	for _, container := range allContainers {
		requests := &container.Resources.Requests
		limits := &container.Resources.Limits
		if !requests.Memory().Equal(*limits.Memory()) {
			score.AddComment(container.Name, "Memory requests does not match limits", "Having equal requests and limits is recommended to avoid resource DDOS of the node during spikes. Set resources.requests.memory == resources.limits.memory")
			resourcesDoNotMatch = true
		}
	}

	if resourcesDoNotMatch {
		score.Grade = scorecard.GradeCritical
	} else {
		score.Grade = scorecard.GradeAllOK
	}

	return
}

// containerImageTag checks that no container is using the ":latest" tag
func containerImageTag(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	pod := ps.GetPodTemplateSpec().Spec

	allContainers := pod.InitContainers
	allContainers = append(allContainers, pod.Containers...)

	hasTagLatest := false

	for _, container := range allContainers {
		tag := containerTag(container.Image)
		if tag == "" || tag == "latest" {
			score.AddComment(container.Name, "Image with latest tag", "Using a fixed tag is recommended to avoid accidental upgrades")
			hasTagLatest = true
		}
	}

	if hasTagLatest {
		score.Grade = scorecard.GradeCritical
	} else {
		score.Grade = scorecard.GradeAllOK
	}

	return
}

// containerImagePullPolicy checks if the containers ImagePullPolicy is set to PullAlways
func containerImagePullPolicy(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	pod := ps.GetPodTemplateSpec().Spec

	allContainers := pod.InitContainers
	allContainers = append(allContainers, pod.Containers...)

	// Default to AllOK
	score.Grade = scorecard.GradeAllOK

	for _, container := range allContainers {
		tag := containerTag(container.Image)

		// If the pull policy is not set, and the tag is either empty or latest
		// kubernetes will default to always pull the image
		if container.ImagePullPolicy == corev1.PullPolicy("") && (tag == "" || tag == "latest") {
			continue
		}

		// No defined pull policy
		if container.ImagePullPolicy != corev1.PullAlways || container.ImagePullPolicy == corev1.PullPolicy("") {
			score.AddComment(container.Name, "ImagePullPolicy is not set to Always", "It's recommended to always set the ImagePullPolicy to Always, to make sure that the imagePullSecrets are always correct, and to always get the image you want.")
			score.Grade = scorecard.GradeCritical
		}
	}

	return
}

// containerTag returns the image tag
// An empty string is returned if the image has no tag
func containerTag(image string) string {
	imageParts := strings.Split(image, ":")
	if len(imageParts) > 1 {
		imageVersion := imageParts[len(imageParts)-1]
		return imageVersion
	}
	return ""
}

func containerStorageEphemeralRequestAndLimit(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	allContainers := ps.GetPodTemplateSpec().Spec.InitContainers
	allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...)

	score.Grade = scorecard.GradeAllOK

	hasMissingLimit := false
	hasMissingRequest := false

	for _, container := range allContainers {
		if container.Resources.Limits.StorageEphemeral().IsZero() {
			score.AddComment(container.Name, "Ephemeral Storage limit is not set",
				"Resource limits are recommended to avoid resource DDOS. Set resources.limits.ephemeral-storage")
			hasMissingLimit = true
		}
		if container.Resources.Requests.StorageEphemeral().IsZero() {
			score.AddComment(container.Name, "Ephemeral Storage request is not set",
				"Resource requests are recommended to make sure the application can start and run without crashing. Set resource.requests.ephemeral-storage")
			hasMissingRequest = true
		}
	}

	switch {
	case len(allContainers) == 0:
		score.Grade = scorecard.GradeCritical
		score.AddComment("", "No containers defined", "")
	case hasMissingLimit:
		score.Grade = scorecard.GradeCritical
	case hasMissingRequest:
		score.Grade = scorecard.GradeWarning
	default:
		score.Grade = scorecard.GradeAllOK
	}

	return
}

func containerStorageEphemeralRequestEqualsLimit(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	allContainers := ps.GetPodTemplateSpec().Spec.InitContainers
	allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...)

	score.Grade = scorecard.GradeAllOK

	for _, container := range allContainers {
		if !container.Resources.Limits.StorageEphemeral().IsZero() && !container.Resources.Requests.StorageEphemeral().IsZero() {
			requests := &container.Resources.Requests
			limits := &container.Resources.Limits
			if !requests.StorageEphemeral().Equal(*limits.StorageEphemeral()) {
				score.AddComment(container.Name, "Ephemeral Storage request does not match limit", "Having equal requests and limits is recommended to avoid node resource DDOS during spikes")
				score.Grade = scorecard.GradeCritical
			}
		}
	}

	return
}

// List of ports to expose from the container. This is primarily informational. Not specifying a port here
// does not prevent it from being exposed. Specifying it does not expose the port outside the cluster; that
// requires a Service object.
func containerPortsCheck(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	const maxPortNameLength = 15

	allContainers := ps.GetPodTemplateSpec().Spec.InitContainers
	allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...)

	score.Grade = scorecard.GradeAllOK

	for _, container := range allContainers {
		names := make(map[string]bool)
		for _, port := range container.Ports {
			if len(port.Name) > 0 {
				if _, ok := names[port.Name]; !ok {
					names[port.Name] = true
				} else {
					score.AddComment(container.Name, "Container Port Check", "Container ports.containerPort named ports must be unique")
					score.Grade = scorecard.GradeCritical
				}
			}
			if len(port.Name) > maxPortNameLength {
				score.AddComment(container.Name, "Container Port Check", "Container port.Name length exceeds maximum permitted characters")
				score.Grade = scorecard.GradeCritical
			}
			if port.ContainerPort == 0 {
				score.AddComment(container.Name, "Container Port Check", "Container ports.containerPort cannot be empty")
				score.Grade = scorecard.GradeCritical
			}
		}
	}

	return
}

// environmentVariableKeyDuplication checks that no duplicated environment variable keys.
func environmentVariableKeyDuplication(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	pod := ps.GetPodTemplateSpec().Spec

	allContainers := pod.InitContainers
	allContainers = append(allContainers, pod.Containers...)

	score.Grade = scorecard.GradeAllOK

	for _, container := range allContainers {
		envs := make(map[string]struct{})
		for _, env := range container.Env {
			if _, duplicated := envs[env.Name]; duplicated {
				msg := fmt.Sprintf("Container environment variable key '%s' is duplicated", env.Name)
				score.AddComment(container.Name, "Environment Variable Key Duplication", msg)
				score.Grade = scorecard.GradeCritical
				continue
			}
			envs[env.Name] = struct{}{}
		}
	}
	return
}
0707010000005C000081A4000000000000000000000001680FB1F800004469000000000000000000000000000000000000003400000000kube-score-1.20.0/score/container/container_test.gopackage container

import (
	"testing"

	ks "github.com/zegl/kube-score/domain"
	"k8s.io/apimachinery/pkg/api/resource"

	"github.com/stretchr/testify/assert"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/zegl/kube-score/scorecard"
)

type podSpeccer struct {
	typeMeta   metav1.TypeMeta
	objectMeta metav1.ObjectMeta
	spec       corev1.PodTemplateSpec
}

func (p *podSpeccer) GetTypeMeta() metav1.TypeMeta {
	return p.typeMeta
}

func (p *podSpeccer) GetObjectMeta() metav1.ObjectMeta {
	return p.objectMeta
}

func (p *podSpeccer) GetPodTemplateSpec() corev1.PodTemplateSpec {
	return p.spec
}

func (p *podSpeccer) FileLocation() ks.FileLocation {
	return ks.FileLocation{}
}

func TestOkAllTheSameContainerResourceRequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerResourceRequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeAllOK, s.Grade)
	assert.Len(t, s.Comments, 0)
}

func TestOkMultipleContainersContainerResourceRequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerResourceRequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					InitContainers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
					},
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
						{
							Name: "foo2",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeAllOK, s.Grade)
	assert.Len(t, s.Comments, 0)
}

func TestOkSameQuantityContainerResourceRequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerResourceRequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1000m"),
									"memory": resource.MustParse("0.25Gi"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeAllOK, s.Grade)
	assert.Len(t, s.Comments, 0)
}

func TestFailBothContainerResourceRequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerResourceRequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("2"),
									"memory": resource.MustParse("512Mi"),
								},
							},
						},
					},
				},
			},
		})

	assert.Equal(t, scorecard.GradeCritical, s.Grade)
	assert.Len(t, s.Comments, 2)
	assert.Equal(t, "foo", s.Comments[0].Path)
	assert.Equal(t, "CPU requests does not match limits", s.Comments[0].Summary)
	assert.Equal(t, "Having equal requests and limits is recommended to avoid resource DDOS of the node during spikes. Set resources.requests.cpu == resources.limits.cpu", s.Comments[0].Description)
	assert.Equal(t, "foo", s.Comments[1].Path)
	assert.Equal(t, "Memory requests does not match limits", s.Comments[1].Summary)
	assert.Equal(t, "Having equal requests and limits is recommended to avoid resource DDOS of the node during spikes. Set resources.requests.memory == resources.limits.memory", s.Comments[1].Description)
}

func TestFailCpuInitContainerResourceRequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerResourceRequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					InitContainers: []corev1.Container{
						{
							Name: "init",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("2"),
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
					},
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeCritical, s.Grade)
	assert.Len(t, s.Comments, 1)
	assert.Equal(t, "init", s.Comments[0].Path)
	assert.Equal(t, "CPU requests does not match limits", s.Comments[0].Summary)
	assert.Equal(t, "Having equal requests and limits is recommended to avoid resource DDOS of the node during spikes. Set resources.requests.cpu == resources.limits.cpu", s.Comments[0].Description)
}

func TestOkAllCPURequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerCPURequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("1"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("1"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeAllOK, s.Grade)
	assert.Len(t, s.Comments, 0)
}

func TestOkMultipleContainersContainerCPURequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerCPURequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					InitContainers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("1"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("1"),
								},
							},
						},
					},
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("1"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("1"),
								},
							},
						},
						{
							Name: "foo2",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("1"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("1"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeAllOK, s.Grade)
	assert.Len(t, s.Comments, 0)
}

func TestOkSameQuantityContainerCPURequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerCPURequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("1"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("1000m"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeAllOK, s.Grade)
	assert.Len(t, s.Comments, 0)
}

func TestFailContainerCPURequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerCPURequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("2"),
									"memory": resource.MustParse("512Mi"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeCritical, s.Grade)
	assert.Len(t, s.Comments, 1)
	assert.Equal(t, "foo", s.Comments[0].Path)
	assert.Equal(t, "CPU requests does not match limits", s.Comments[0].Summary)
	assert.Equal(t, "Having equal requests and limits is recommended to avoid resource DDOS of the node during spikes. Set resources.requests.cpu == resources.limits.cpu", s.Comments[0].Description)

}

func TestFailInitContainerCPURequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerCPURequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					InitContainers: []corev1.Container{
						{
							Name: "init",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("1"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu": resource.MustParse("2"),
								},
							},
						},
					},
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeCritical, s.Grade)
	assert.Len(t, s.Comments, 1)
	assert.Equal(t, "init", s.Comments[0].Path)
	assert.Equal(t, "CPU requests does not match limits", s.Comments[0].Summary)
	assert.Equal(t, "Having equal requests and limits is recommended to avoid resource DDOS of the node during spikes. Set resources.requests.cpu == resources.limits.cpu", s.Comments[0].Description)
}

func TestOkContainerMemoryResourceRequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerMemoryRequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeAllOK, s.Grade)
	assert.Len(t, s.Comments, 0)
}

func TestOkMultipleContainersContainerMemoryRequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerMemoryRequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					InitContainers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
					},
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
						{
							Name: "foo2",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeAllOK, s.Grade)
	assert.Len(t, s.Comments, 0)
}

func TestOkSameQuantityContainerMemoryRequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerMemoryRequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("0.25Gi"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeAllOK, s.Grade)
	assert.Len(t, s.Comments, 0)
}

func TestFailContainerMemoryRequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerMemoryRequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("2"),
									"memory": resource.MustParse("512Mi"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeCritical, s.Grade)
	assert.Len(t, s.Comments, 1)
	assert.Equal(t, "foo", s.Comments[0].Path)
	assert.Equal(t, "Memory requests does not match limits", s.Comments[0].Summary)
	assert.Equal(t, "Having equal requests and limits is recommended to avoid resource DDOS of the node during spikes. Set resources.requests.memory == resources.limits.memory", s.Comments[0].Description)
}

func TestFailInitContainerMemoryRequestsEqualLimits(t *testing.T) {
	t.Parallel()
	s, _ := containerMemoryRequestsEqualLimits(
		&podSpeccer{
			spec: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					InitContainers: []corev1.Container{
						{
							Name: "init",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"memory": resource.MustParse("512Mi"),
								},
							},
						},
					},
					Containers: []corev1.Container{
						{
							Name: "foo",
							Resources: corev1.ResourceRequirements{
								Requests: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
								Limits: map[corev1.ResourceName]resource.Quantity{
									"cpu":    resource.MustParse("1"),
									"memory": resource.MustParse("256Mi"),
								},
							},
						},
					},
				},
			},
		},
	)

	assert.Equal(t, scorecard.GradeCritical, s.Grade)
	assert.Len(t, s.Comments, 1)
	assert.Equal(t, "init", s.Comments[0].Path)
	assert.Equal(t, "Memory requests does not match limits", s.Comments[0].Summary)
	assert.Equal(t, "Having equal requests and limits is recommended to avoid resource DDOS of the node during spikes. Set resources.requests.memory == resources.limits.memory", s.Comments[0].Description)
}
0707010000005D000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002000000000kube-score-1.20.0/score/cronjob0707010000005E000081A4000000000000000000000001680FB1F800000679000000000000000000000000000000000000002B00000000kube-score-1.20.0/score/cronjob/cronjob.gopackage cronjob

import (
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"
)

func Register(allChecks *checks.Checks) {
	allChecks.RegisterCronJobCheck("CronJob has deadline", `Makes sure that all CronJobs has a configured deadline`, cronJobHasDeadline)
	allChecks.RegisterCronJobCheck("CronJob RestartPolicy", `Makes sure CronJobs have a valid RestartPolicy`, cronJobHasRestartPolicy)
}

func cronJobHasDeadline(job ks.CronJob) (score scorecard.TestScore, err error) {
	if job.StartingDeadlineSeconds() == nil {
		score.Grade = scorecard.GradeCritical
		score.AddComment("", "The CronJob should have startingDeadlineSeconds configured",
			"This makes sure that jobs are automatically cancelled if they can not be scheduled")
		return
	}

	score.Grade = scorecard.GradeAllOK
	return
}

// CronJob restartPolicy must be "OnFailure" or "Never". It cannot be empty (unspecified)
func cronJobHasRestartPolicy(job ks.CronJob) (score scorecard.TestScore, err error) {
	podTmpl := job.GetPodTemplateSpec()
	restartPolicy := podTmpl.Spec.RestartPolicy

	if len(restartPolicy) > 0 {
		if restartPolicy == "Never" || restartPolicy == "OnFailure" {
			score.Grade = scorecard.GradeAllOK
		} else {
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "The CronJob must have a valid RestartPolicy configured",
				"Valid CronJob RestartPolicy settings are Never or OnFailure")
		}
	} else {
		score.Grade = scorecard.GradeCritical
		score.AddComment("", "The CronJob is missing a valid RestartPolicy",
			"Valid CronJob RestartPolicy settings are Never or OnFailure")
	}

	return
}
0707010000005F000081A4000000000000000000000001680FB1F8000006CD000000000000000000000000000000000000002800000000kube-score-1.20.0/score/cronjob_test.gopackage score

import (
	"testing"

	"github.com/zegl/kube-score/scorecard"
)

func TestCronJobHasDeadline(t *testing.T) {
	t.Parallel()

	for _, v := range []string{"batchv1beta1", "batchv1"} {
		t.Run(v, func(t *testing.T) {
			testExpectedScore(t, "cronjob-"+v+"-deadline-set.yaml", "CronJob has deadline", scorecard.GradeAllOK)
		})
	}
}

func TestCronJobNotHasDeadline(t *testing.T) {
	t.Parallel()

	for _, v := range []string{"batchv1beta1", "batchv1"} {
		t.Run(v, func(t *testing.T) {
			testExpectedScore(t, "cronjob-"+v+"-deadline-not-set.yaml", "CronJob has deadline", scorecard.GradeCritical)
		})
	}
}

func TestProbesPodCronMissingReady(t *testing.T) {
	t.Parallel()

	for _, v := range []string{"batchv1beta1", "batchv1"} {
		t.Run(v, func(t *testing.T) {
			testExpectedScore(t, "cronjob-"+v+"-deadline-not-set.yaml", "Pod Probes", scorecard.GradeAllOK)
		})
	}
}

func TestCronJobHasRestartPolicyMissing(t *testing.T) {
	t.Parallel()

	for _, v := range []string{"batchv1beta1", "batchv1"} {
		t.Run(v, func(t *testing.T) {
			testExpectedScore(t, "cronjob-"+v+"-restartpolicy-not-set.yaml", "CronJob RestartPolicy", scorecard.GradeCritical)
		})
	}
}

func TestCronJobHasRestartPolicyInvalid(t *testing.T) {
	t.Parallel()

	for _, v := range []string{"batchv1beta1", "batchv1"} {
		t.Run(v, func(t *testing.T) {
			testExpectedScore(t, "cronjob-"+v+"-restartpolicy-invalid.yaml", "CronJob RestartPolicy", scorecard.GradeCritical)
		})
	}
}

func TestCronJobHasRestartPolicyValid(t *testing.T) {
	t.Parallel()

	for _, v := range []string{"batchv1beta1", "batchv1"} {
		t.Run(v, func(t *testing.T) {
			testExpectedScore(t, "cronjob-"+v+"-restartpolicy-valid.yaml", "CronJob RestartPolicy", scorecard.GradeAllOK)
		})
	}
}
07070100000060000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002300000000kube-score-1.20.0/score/deployment07070100000061000081A4000000000000000000000001680FB1F8000011DC000000000000000000000000000000000000003100000000kube-score-1.20.0/score/deployment/deployment.gopackage deployment

import (
	"fmt"
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/score/internal"
	"github.com/zegl/kube-score/scorecard"
	v1 "k8s.io/api/apps/v1"
	autoscalingv1 "k8s.io/api/autoscaling/v1"
	"k8s.io/utils/ptr"
)

func Register(allChecks *checks.Checks, all ks.AllTypes, minReplicas int) {
	allChecks.RegisterDeploymentCheck("Deployment Strategy", `Makes sure that all Deployments targeted by service use RollingUpdate strategy`, deploymentRolloutStrategy(all.Services()))
	allChecks.RegisterDeploymentCheck("Deployment Replicas", `Makes sure that Deployment has multiple replicas`, deploymentReplicas(all.Services(), all.HorizontalPodAutoscalers(), minReplicas))
}

// deploymentRolloutStrategy checks if a Deployment has the update strategy on RollingUpdate if targeted by a service
func deploymentRolloutStrategy(svcs []ks.Service) func(deployment v1.Deployment) (scorecard.TestScore, error) {
	svcsInNamespace := make(map[string][]map[string]string)
	for _, s := range svcs {
		svc := s.Service()
		if _, ok := svcsInNamespace[svc.Namespace]; !ok {
			svcsInNamespace[svc.Namespace] = []map[string]string{}
		}
		svcsInNamespace[svc.Namespace] = append(svcsInNamespace[svc.Namespace], svc.Spec.Selector)
	}
	return func(deployment v1.Deployment) (score scorecard.TestScore, err error) {
		referencedByService := false

		for _, svcSelector := range svcsInNamespace[deployment.Namespace] {
			if internal.LabelSelectorMatchesLabels(svcSelector, deployment.Spec.Template.Labels) {
				referencedByService = true
				break
			}
		}

		if referencedByService {
			if deployment.Spec.Strategy.Type == v1.RollingUpdateDeploymentStrategyType || deployment.Spec.Strategy.Type == "" {
				score.Grade = scorecard.GradeAllOK
			} else {
				score.Grade = scorecard.GradeWarning
				score.AddCommentWithURL("", "Deployment update strategy", "The deployment is used by a service but not using the RollingUpdate strategy which can cause interruptions. Set .spec.strategy.type to RollingUpdate.", "https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy")
			}
		} else {
			score.Skipped = true
			score.AddComment("", "Skipped as the Deployment is not targeted by a service", "")
		}

		return
	}
}

// deploymentReplicas checks if a Deployment has >= 2 replicas if not (targeted by service || has HorizontalPodAutoscaler)
func deploymentReplicas(svcs []ks.Service, hpas []ks.HpaTargeter, minReplicas int) func(deployment v1.Deployment) (scorecard.TestScore, error) {
	svcsInNamespace := make(map[string][]map[string]string)
	for _, s := range svcs {
		svc := s.Service()
		if _, ok := svcsInNamespace[svc.Namespace]; !ok {
			svcsInNamespace[svc.Namespace] = []map[string]string{}
		}
		svcsInNamespace[svc.Namespace] = append(svcsInNamespace[svc.Namespace], svc.Spec.Selector)
	}
	hpasInNamespace := make(map[string][]autoscalingv1.CrossVersionObjectReference)
	for _, hpa := range hpas {
		hpaTarget := hpa.HpaTarget()
		hpaMeta := hpa.GetObjectMeta()
		if _, ok := hpasInNamespace[hpaMeta.Namespace]; !ok {
			hpasInNamespace[hpaMeta.Namespace] = []autoscalingv1.CrossVersionObjectReference{}
		}
		hpasInNamespace[hpaMeta.Namespace] = append(hpasInNamespace[hpaMeta.Namespace], hpaTarget)
	}

	return func(deployment v1.Deployment) (score scorecard.TestScore, err error) {
		referencedByService := false
		hasHPA := false

		for _, svcSelector := range svcsInNamespace[deployment.Namespace] {
			if internal.LabelSelectorMatchesLabels(svcSelector, deployment.Spec.Template.Labels) {
				referencedByService = true
				break
			}
		}

		for _, hpaTarget := range hpasInNamespace[deployment.Namespace] {
			if deployment.TypeMeta.APIVersion == hpaTarget.APIVersion &&
				deployment.TypeMeta.Kind == hpaTarget.Kind &&
				deployment.ObjectMeta.Name == hpaTarget.Name {
				hasHPA = true
				break
			}
		}

		if !referencedByService {
			score.Skipped = true
			score.AddComment("", "Skipped as the Deployment is not targeted by service", "")
		} else if hasHPA {
			score.Skipped = true
			score.AddComment("", "Skipped as the Deployment is controlled by a HorizontalPodAutoscaler", "")
		} else {
			if ptr.Deref(deployment.Spec.Replicas, 1) >= int32(minReplicas) {
				score.Grade = scorecard.GradeAllOK
			} else {
				score.Grade = scorecard.GradeWarning
				score.AddComment("", "Deployment few replicas", fmt.Sprintf("Deployments targeted by Services are recommended to have at least %d replicas to prevent unwanted downtime.", minReplicas))
			}
		}

		return
	}
}
07070100000062000081A4000000000000000000000001680FB1F800000B73000000000000000000000000000000000000002B00000000kube-score-1.20.0/score/deployment_test.gopackage score

import (
	"testing"

	"github.com/zegl/kube-score/config"

	"github.com/stretchr/testify/assert"
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
)

func TestServiceTargetsDeploymentStrategyRolling(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-target-deployment.yaml", "Deployment Strategy", scorecard.GradeAllOK)
}

func TestServiceNotTargetsDeploymentStrategyNotRelevant(t *testing.T) {
	t.Parallel()
	skipped := wasSkipped(t,
		[]ks.NamedReader{testFile("service-not-target-deployment.yaml")}, nil, nil,
		"Deployment Strategy")
	assert.True(t, skipped)
}

func TestServiceTargetsDeploymentStrategyNotRolling(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-target-deployment-not-rolling.yaml", "Deployment Strategy", scorecard.GradeWarning)
}

func TestServiceTargetsDeploymentStrategyNotSet(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-target-deployment-strategy-not-set.yaml", "Deployment Strategy", scorecard.GradeAllOK)
}

func TestServiceTargetsDeploymentReplicasOk(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-target-deployment.yaml", "Deployment Replicas", scorecard.GradeAllOK)
}

func TestServiceNotTargetsDeploymentReplicasNotRelevant(t *testing.T) {
	t.Parallel()
	assert.True(t, wasSkipped(t,
		[]ks.NamedReader{testFile("service-not-target-deployment.yaml")}, nil, nil,
		"Deployment Replicas"))

	summaries := getSummaries(t, []ks.NamedReader{testFile("service-not-target-deployment.yaml")}, nil, nil,
		"Deployment Replicas")
	assert.Contains(t, summaries, "Skipped as the Deployment is not targeted by service")
}

func TestServiceTargetsDeploymentReplicasNok(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("service-target-deployment-replica-1.yaml")}, nil, &config.RunConfiguration{
		MinReplicasDeployment: 2,
	}, "Deployment Replicas", scorecard.GradeWarning)
}

func TestServiceTargetsDeploymentReplicasNok1(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("service-target-deployment-replica-1.yaml")}, nil, &config.RunConfiguration{
		MinReplicasDeployment: 1,
	}, "Deployment Replicas", scorecard.GradeAllOK)
}

func TestServiceTargetsDeploymentReplicasNok5(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("service-target-deployment-replica-1.yaml")}, nil, &config.RunConfiguration{
		MinReplicasDeployment: 5,
	}, "Deployment Replicas", scorecard.GradeWarning)
}

func TestHPATargetsDeployment(t *testing.T) {
	t.Parallel()
	assert.True(t, wasSkipped(t,
		[]ks.NamedReader{testFile("hpa-target-deployment.yaml")}, nil, nil,
		"Deployment Replicas"))

	summaries := getSummaries(t, []ks.NamedReader{testFile("hpa-target-deployment.yaml")}, nil, nil,
		"Deployment Replicas")
	assert.Contains(t, summaries, "Skipped as the Deployment is controlled by a HorizontalPodAutoscaler")
}
07070100000063000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002900000000kube-score-1.20.0/score/disruptionbudget07070100000064000081A4000000000000000000000001680FB1F800000F82000000000000000000000000000000000000003D00000000kube-score-1.20.0/score/disruptionbudget/disruptionbudget.gopackage disruptionbudget

import (
	"fmt"

	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/score/internal"
	"github.com/zegl/kube-score/scorecard"

	appsv1 "k8s.io/api/apps/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func Register(allChecks *checks.Checks, budgets ks.PodDisruptionBudgets) {
	allChecks.RegisterStatefulSetCheck("StatefulSet has PodDisruptionBudget", `Makes sure that all StatefulSets are targeted by a PDB`, statefulSetHas(budgets.PodDisruptionBudgets()))
	allChecks.RegisterDeploymentCheck("Deployment has PodDisruptionBudget", `Makes sure that all Deployments are targeted by a PDB`, deploymentHas(budgets.PodDisruptionBudgets()))
	allChecks.RegisterPodDisruptionBudgetCheck("PodDisruptionBudget has policy", `Makes sure that PodDisruptionBudgets specify minAvailable or maxUnavailable`, hasPolicy)
}

func hasMatching(budgets []ks.PodDisruptionBudget, namespace string, labels map[string]string) (bool, string, error) {
	var hasNamespaceMismatch []string
	for _, budget := range budgets {

		selector, err := metav1.LabelSelectorAsSelector(budget.PodDisruptionBudgetSelector())
		if err != nil {
			return false, "", fmt.Errorf("failed to create selector: %w", err)
		}

		if !selector.Matches(internal.MapLabels(labels)) {
			continue
		}

		// matches, but in different namespace
		if budget.Namespace() != namespace {
			hasNamespaceMismatch = append(hasNamespaceMismatch, budget.Namespace())
			continue
		}

		return true, "", nil
	}

	if len(hasNamespaceMismatch) > 0 {
		return false, fmt.Sprintf("A matching budget was found, but in a different namespace. expected='%s' got='%+v'", namespace, hasNamespaceMismatch), nil
	}

	return false, "", nil
}

func statefulSetHas(budgets []ks.PodDisruptionBudget) func(appsv1.StatefulSet) (scorecard.TestScore, error) {
	return func(statefulset appsv1.StatefulSet) (score scorecard.TestScore, err error) {
		if statefulset.Spec.Replicas != nil && *statefulset.Spec.Replicas < 2 {
			score.Skipped = true
			score.AddComment("", "Skipped because the statefulset has less than 2 replicas", "")
			return
		}

		match, comment, matchErr := hasMatching(budgets, statefulset.Namespace, statefulset.Spec.Template.Labels)
		if matchErr != nil {
			err = matchErr
			return
		}

		if match {
			score.Grade = scorecard.GradeAllOK
		} else {
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "No matching PodDisruptionBudget was found", "It's recommended to define a PodDisruptionBudget to avoid unexpected downtime during Kubernetes maintenance operations, such as when draining a node. "+comment)
		}

		return
	}
}

func deploymentHas(budgets []ks.PodDisruptionBudget) func(appsv1.Deployment) (scorecard.TestScore, error) {
	return func(deployment appsv1.Deployment) (score scorecard.TestScore, err error) {
		if deployment.Spec.Replicas != nil && *deployment.Spec.Replicas < 2 {
			score.Skipped = true
			score.AddComment("", "Skipped because the deployment has less than 2 replicas", "")
			return
		}

		match, comment, matchErr := hasMatching(budgets, deployment.Namespace, deployment.Spec.Template.Labels)
		if matchErr != nil {
			err = matchErr
			return
		}

		if match {
			score.Grade = scorecard.GradeAllOK
		} else {
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "No matching PodDisruptionBudget was found", "It's recommended to define a PodDisruptionBudget to avoid unexpected downtime during Kubernetes maintenance operations, such as when draining a node. "+comment)
		}

		return
	}
}

func hasPolicy(pdb ks.PodDisruptionBudget) (score scorecard.TestScore, err error) {
	spec := pdb.Spec()
	if spec.MinAvailable == nil && spec.MaxUnavailable == nil {
		score.AddComment("", "PodDisruptionBudget missing policy", "PodDisruptionBudget should specify minAvailable or maxUnavailable.")
		score.Grade = scorecard.GradeCritical
	} else {
		score.Grade = scorecard.GradeAllOK
	}

	return
}
07070100000065000081A4000000000000000000000001680FB1F800000665000000000000000000000000000000000000004200000000kube-score-1.20.0/score/disruptionbudget/disruptionbudget_test.gopackage disruptionbudget

import (
	"testing"

	"github.com/stretchr/testify/assert"
	appsv1 "k8s.io/api/apps/v1"

	"github.com/zegl/kube-score/scorecard"
)

func TestStatefulSetReplicas(t *testing.T) {
	t.Parallel()
	cases := map[*int32]struct {
		grade   scorecard.Grade
		skipped bool
	}{
		nil:        {scorecard.GradeCritical, false}, // failed
		intptr(1):  {0, true},                        // skipped
		intptr(10): {scorecard.GradeCritical, false}, // failed
	}

	fn := statefulSetHas(nil)

	for replicas, expected := range cases {
		res, err := fn(appsv1.StatefulSet{Spec: appsv1.StatefulSetSpec{Replicas: replicas}})
		assert.Nil(t, err)

		assert.Equal(t, expected.skipped, res.Skipped)

		if replicas == nil {
			assert.Equal(t, expected.grade, res.Grade, "replicas=nil")
		} else {
			assert.Equal(t, expected.grade, res.Grade, "replicas=%+v", *replicas)
		}
	}
}

func TestDeploymentReplicas(t *testing.T) {
	t.Parallel()
	cases := map[*int32]struct {
		grade   scorecard.Grade
		skipped bool
	}{
		nil:        {scorecard.GradeCritical, false}, // failed
		intptr(1):  {0, true},                        // skipped
		intptr(10): {scorecard.GradeCritical, false}, // failed
	}

	fn := deploymentHas(nil)

	for replicas, expected := range cases {
		res, err := fn(appsv1.Deployment{Spec: appsv1.DeploymentSpec{Replicas: replicas}})
		assert.Nil(t, err)

		assert.Equal(t, expected.skipped, res.Skipped)

		if replicas == nil {
			assert.Equal(t, expected.grade, res.Grade, "replicas=nil")
		} else {
			assert.Equal(t, expected.grade, res.Grade, "replicas=%+v", *replicas)
		}
	}
}

func intptr(a int32) *int32 {
	return &a
}
07070100000066000081A4000000000000000000000001680FB1F80000043B000000000000000000000000000000000000002D00000000kube-score-1.20.0/score/filelocation_test.gopackage score

import (
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/zegl/kube-score/config"
	ks "github.com/zegl/kube-score/domain"
)

func TestFileLocationHelm(t *testing.T) {
	sc, err := testScore([]ks.NamedReader{testFile("linenumbers-helm.yaml")}, nil, &config.RunConfiguration{
		KubernetesVersion: config.Semver{Major: 1, Minor: 18},
	})
	assert.Nil(t, err)
	for _, c := range sc {
		assert.Equal(t, "app1/templates/deployment.yaml", c.FileLocation.Name)
	}
	assert.Equal(t, 1, sc["Deployment/apps/v1//foo"].FileLocation.Line)
	assert.Equal(t, 1, sc["Deployment/apps/v1//foo2"].FileLocation.Line)
}

func TestFileLocation(t *testing.T) {
	sc, err := testScore([]ks.NamedReader{testFile("linenumbers.yaml")}, nil, &config.RunConfiguration{
		KubernetesVersion: config.Semver{Major: 1, Minor: 18},
	})
	assert.Nil(t, err)
	for _, c := range sc {
		assert.Equal(t, "testdata/linenumbers.yaml", c.FileLocation.Name)
	}
	assert.Equal(t, 2, sc["Deployment/apps/v1//foo"].FileLocation.Line)
	assert.Equal(t, 12, sc["Deployment/apps/v1//foo2"].FileLocation.Line)
}
07070100000067000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001C00000000kube-score-1.20.0/score/hpa07070100000068000081A4000000000000000000000001680FB1F800000741000000000000000000000000000000000000002300000000kube-score-1.20.0/score/hpa/hpa.gopackage hpa

import (
	"fmt"
	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"
	"k8s.io/utils/ptr"
)

func Register(allChecks *checks.Checks, allTargetableObjs []domain.BothMeta, minReplicas int) {
	allChecks.RegisterHorizontalPodAutoscalerCheck("HorizontalPodAutoscaler has target", `Makes sure that the HPA targets a valid object`, hpaHasTarget(allTargetableObjs))
	allChecks.RegisterHorizontalPodAutoscalerCheck("HorizontalPodAutoscaler Replicas", `Makes sure that the HPA has multiple replicas`, hpaHasMultipleReplicas(minReplicas))
}

func hpaHasTarget(allTargetableObjs []domain.BothMeta) func(hpa domain.HpaTargeter) (scorecard.TestScore, error) {
	return func(hpa domain.HpaTargeter) (score scorecard.TestScore, err error) {
		targetRef := hpa.HpaTarget()
		var hasTarget bool
		for _, t := range allTargetableObjs {
			if t.TypeMeta.APIVersion == targetRef.APIVersion &&
				t.TypeMeta.Kind == targetRef.Kind &&
				t.ObjectMeta.Name == targetRef.Name &&
				t.ObjectMeta.Namespace == hpa.GetObjectMeta().Namespace {
				hasTarget = true
				break
			}
		}

		if hasTarget {
			score.Grade = scorecard.GradeAllOK
		} else {
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "The HPA target does not match anything", "")
		}
		return
	}
}

func hpaHasMultipleReplicas(minReplicas int) func(hpa domain.HpaTargeter) (scorecard.TestScore, error) {
	return func(hpa domain.HpaTargeter) (score scorecard.TestScore, err error) {
		if ptr.Deref(hpa.MinReplicas(), 1) >= int32(minReplicas) {
			score.Grade = scorecard.GradeAllOK
		} else {
			score.Grade = scorecard.GradeWarning
			score.AddComment("", "HPA few replicas", fmt.Sprintf("HorizontalPodAutoscalers are recommended to have at least %d replicas to prevent unwanted downtime.", minReplicas))
		}
		return
	}
}
07070100000069000081A4000000000000000000000001680FB1F800001257000000000000000000000000000000000000002800000000kube-score-1.20.0/score/hpa/hpa_test.gopackage hpa

import (
	"testing"

	"github.com/stretchr/testify/assert"
	v1 "k8s.io/api/autoscaling/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
)

func TestHpaHasTarget(t *testing.T) {
	t.Parallel()
	testcases := []struct {
		hpa           v1.HorizontalPodAutoscaler
		allTargets    []domain.BothMeta
		expectedGrade scorecard.Grade
	}{
		// No match
		{
			hpa: v1.HorizontalPodAutoscaler{
				Spec: v1.HorizontalPodAutoscalerSpec{
					ScaleTargetRef: v1.CrossVersionObjectReference{
						Kind:       "Deployment",
						Name:       "foo",
						APIVersion: "apps/v1",
					},
				},
			},
			expectedGrade: scorecard.GradeCritical,
		},

		// Match (no namespace)
		{
			hpa: v1.HorizontalPodAutoscaler{
				Spec: v1.HorizontalPodAutoscalerSpec{
					ScaleTargetRef: v1.CrossVersionObjectReference{
						Kind:       "Deployment",
						Name:       "foo",
						APIVersion: "apps/v1",
					},
				},
			},
			allTargets: []domain.BothMeta{
				{
					TypeMeta:   metav1.TypeMeta{Kind: "Deployment", APIVersion: "apps/v1"},
					ObjectMeta: metav1.ObjectMeta{Name: "foo"},
				},
			},
			expectedGrade: scorecard.GradeAllOK,
		},

		// Match (namespace)
		{
			hpa: v1.HorizontalPodAutoscaler{
				ObjectMeta: metav1.ObjectMeta{Namespace: "foospace"},
				Spec: v1.HorizontalPodAutoscalerSpec{
					ScaleTargetRef: v1.CrossVersionObjectReference{
						Kind:       "Deployment",
						Name:       "foo",
						APIVersion: "apps/v1",
					},
				},
			},
			allTargets: []domain.BothMeta{
				{
					TypeMeta:   metav1.TypeMeta{Kind: "Deployment", APIVersion: "apps/v1"},
					ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foospace"},
				},
			},
			expectedGrade: scorecard.GradeAllOK,
		},

		// No match (namespace)
		{
			hpa: v1.HorizontalPodAutoscaler{
				ObjectMeta: metav1.ObjectMeta{Namespace: "foospace2"},
				Spec: v1.HorizontalPodAutoscalerSpec{
					ScaleTargetRef: v1.CrossVersionObjectReference{
						Kind:       "Deployment",
						Name:       "foo",
						APIVersion: "apps/v1",
					},
				},
			},
			allTargets: []domain.BothMeta{
				{
					TypeMeta:   metav1.TypeMeta{Kind: "Deployment", APIVersion: "apps/v1"},
					ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foospace"},
				},
			},
			expectedGrade: scorecard.GradeCritical,
		},

		// No match (name)
		{
			hpa: v1.HorizontalPodAutoscaler{
				ObjectMeta: metav1.ObjectMeta{Namespace: "foospace"},
				Spec: v1.HorizontalPodAutoscalerSpec{
					ScaleTargetRef: v1.CrossVersionObjectReference{
						Kind:       "Deployment",
						Name:       "not-foo",
						APIVersion: "apps/v1",
					},
				},
			},
			allTargets: []domain.BothMeta{
				{
					TypeMeta:   metav1.TypeMeta{Kind: "Deployment", APIVersion: "apps/v1"},
					ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foospace"},
				},
			},
			expectedGrade: scorecard.GradeCritical,
		},

		// No match (kind)
		{
			hpa: v1.HorizontalPodAutoscaler{
				ObjectMeta: metav1.ObjectMeta{Namespace: "foospace"},
				Spec: v1.HorizontalPodAutoscalerSpec{
					ScaleTargetRef: v1.CrossVersionObjectReference{
						Kind:       "ReplicaSet",
						Name:       "foo",
						APIVersion: "apps/v1",
					},
				},
			},
			allTargets: []domain.BothMeta{
				{
					TypeMeta:   metav1.TypeMeta{Kind: "Deployment", APIVersion: "apps/v1"},
					ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foospace"},
				},
			},
			expectedGrade: scorecard.GradeCritical,
		},

		// No match (version)
		{
			hpa: v1.HorizontalPodAutoscaler{
				ObjectMeta: metav1.ObjectMeta{Namespace: "foospace"},
				Spec: v1.HorizontalPodAutoscalerSpec{
					ScaleTargetRef: v1.CrossVersionObjectReference{
						Kind:       "Deployment",
						Name:       "foo",
						APIVersion: "apps/v1beta1",
					},
				},
			},
			allTargets: []domain.BothMeta{
				{
					TypeMeta:   metav1.TypeMeta{Kind: "Deployment", APIVersion: "apps/v1"},
					ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foospace"},
				},
			},
			expectedGrade: scorecard.GradeCritical,
		},
	}

	for _, tc := range testcases {
		fn := hpaHasTarget(tc.allTargets)
		score, _ := fn(hpav1{tc.hpa})
		assert.Equal(t, tc.expectedGrade, score.Grade)
	}
}

type hpav1 struct {
	v1.HorizontalPodAutoscaler
}

func (d hpav1) GetTypeMeta() metav1.TypeMeta {
	return d.TypeMeta
}

func (d hpav1) GetObjectMeta() metav1.ObjectMeta {
	return d.ObjectMeta
}

func (d hpav1) MinReplicas() *int32 {
	return d.Spec.MinReplicas
}

func (d hpav1) HpaTarget() v1.CrossVersionObjectReference {
	return d.Spec.ScaleTargetRef
}

func (d hpav1) FileLocation() domain.FileLocation {
	return domain.FileLocation{}
}
0707010000006A000081A4000000000000000000000001680FB1F800000728000000000000000000000000000000000000002400000000kube-score-1.20.0/score/hpa_test.gopackage score

import (
	"testing"

	"github.com/zegl/kube-score/config"
	ks "github.com/zegl/kube-score/domain"

	"github.com/zegl/kube-score/scorecard"
)

func TestHorizontalPodAutoscalerV1TargetsDeployment(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "hpa-autoscalingv1-targets-deployment.yaml", "HorizontalPodAutoscaler has target", scorecard.GradeAllOK)
}

func TestHorizontalPodAutoscalerV2TargetsDeployment(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "hpa-autoscalingv2-targets-deployment.yaml", "HorizontalPodAutoscaler has target", scorecard.GradeAllOK)
}

func TestHorizontalPodAutoscalerHasNoTarget(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "hpa-has-no-target.yaml", "HorizontalPodAutoscaler has target", scorecard.GradeCritical)
}

func TestHorizontalPodAutoscalerMinReplicasOk(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "hpa-min-replicas-ok.yaml", "HorizontalPodAutoscaler Replicas", scorecard.GradeAllOK)
}

func TestHorizontalPodAutoscalerMinReplicasNok(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("hpa-min-replicas-nok.yaml")}, nil, &config.RunConfiguration{
		MinReplicasHPA: 2,
	}, "HorizontalPodAutoscaler Replicas", scorecard.GradeWarning)
}

func TestHorizontalPodAutoscalerMinReplicasMin5ReplicasNotOk(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("hpa-min-replicas-nok.yaml")}, nil, &config.RunConfiguration{
		MinReplicasHPA: 5,
	}, "HorizontalPodAutoscaler Replicas", scorecard.GradeWarning)
}

func TestHorizontalPodAutoscalerMinReplicasMin1ReplicaOk(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("hpa-min-replicas-nok.yaml")}, nil, &config.RunConfiguration{
		MinReplicasHPA: 1,
	}, "HorizontalPodAutoscaler Replicas", scorecard.GradeAllOK)
}
0707010000006B000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002000000000kube-score-1.20.0/score/ingress0707010000006C000081A4000000000000000000000001680FB1F8000008E8000000000000000000000000000000000000002B00000000kube-score-1.20.0/score/ingress/ingress.gopackage ingress

import (
	"fmt"

	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"
)

func Register(allChecks *checks.Checks, services ks.Services) {
	allChecks.RegisterIngressCheck("Ingress targets Service", `Makes sure that the Ingress targets a Service`, ingressTargetsService(services.Services()))
}

func ingressTargetsService(allServices []ks.Service) func(ks.Ingress) (scorecard.TestScore, error) {
	return func(ingress ks.Ingress) (scorecard.TestScore, error) {
		return ingressTargetsServiceCommon(ingress, allServices)
	}
}

func ingressTargetsServiceCommon(ingress ks.Ingress, allServices []ks.Service) (score scorecard.TestScore, err error) {
	allRulesHaveMatches := true

	for _, rule := range ingress.Rules() {
		if rule.IngressRuleValue.HTTP == nil {
			continue
		}

		for _, path := range rule.IngressRuleValue.HTTP.Paths {

			pathHasMatch := false

			for _, srv := range allServices {
				service := srv.Service()

				if service.Namespace != ingress.GetObjectMeta().Namespace {
					continue
				}
				if path.Backend.Service == nil {
					continue
				}

				if service.Name == path.Backend.Service.Name {
					for _, servicePort := range service.Spec.Ports {
						if path.Backend.Service.Port.Number > 0 && servicePort.Port == path.Backend.Service.Port.Number {
							pathHasMatch = true
						} else if servicePort.Name == path.Backend.Service.Port.Name {
							pathHasMatch = true
						}
					}
				}
			}

			if !pathHasMatch {
				allRulesHaveMatches = false
				if path.Backend.Service != nil {
					if path.Backend.Service.Port.Number > 0 {
						score.AddComment(path.Path, "No service match was found", fmt.Sprintf("No service with name %s and port number %d was found", path.Backend.Service.Name, path.Backend.Service.Port.Number))
					} else {
						score.AddComment(path.Path, "No service match was found", fmt.Sprintf("No service with name %s and port named %s was found", path.Backend.Service.Name, path.Backend.Service.Port.Name))
					}
				} else {
					score.AddComment(path.Path, "No service match was found", "")
				}
			}
		}
	}

	if allRulesHaveMatches {
		score.Grade = scorecard.GradeAllOK
	} else {
		score.Grade = scorecard.GradeCritical
	}

	return
}
0707010000006D000081A4000000000000000000000001680FB1F8000006E3000000000000000000000000000000000000002800000000kube-score-1.20.0/score/ingress_test.gopackage score

import (
	"testing"

	"github.com/zegl/kube-score/scorecard"
)

func TestIngressTargetsService(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "ingress-targets-service.yaml", "Ingress targets Service", scorecard.GradeAllOK)
}

func TestIngressTargetsServiceNoMatch(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "ingress-targets-service-no-match.yaml", "Ingress targets Service", scorecard.GradeCritical)
}

func TestNetworkingIngressV1beta1TargetsService(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "ingress-networkingv1beta1-targets-service.yaml", "Ingress targets Service", scorecard.GradeAllOK)
}

func TestNetworkingIngressV1beta1TargetsServiceNoMatch(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "ingress-networkingv1beta1-targets-service-no-match.yaml", "Ingress targets Service", scorecard.GradeCritical)
}

func TestNetworkingIngressV1TargetsService(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "ingress-networkingv1-targets-service.yaml", "Ingress targets Service", scorecard.GradeAllOK)
}

func TestNetworkingIngressV1TargetsServiceNoMatch(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "ingress-networkingv1-targets-service-no-match.yaml", "Ingress targets Service", scorecard.GradeCritical)
}

func TestNetworkPolicyV1InvalidBackend(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "ingress-v1-invalid-backend.yaml", "Ingress targets Service", scorecard.GradeCritical)
}

func TestIngressNoPanicIssue363(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "ingress_issue363.yaml", "Ingress targets Service", scorecard.GradeCritical)
}

func TestIngressNoPanicIssue388(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "ingress_issue388.yaml", "Ingress targets Service", scorecard.GradeAllOK)
}
0707010000006E000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002100000000kube-score-1.20.0/score/internal0707010000006F000081A4000000000000000000000001680FB1F80000026A000000000000000000000000000000000000003200000000kube-score-1.20.0/score/internal/labelselector.gopackage internal

import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

// MapLabels implements the Kubernetes Labels interface
type MapLabels map[string]string

func (l MapLabels) Has(key string) bool {
	_, ok := l[key]
	return ok
}

func (l MapLabels) Get(key string) string {
	return l[key]
}

func LabelSelectorMatchesLabels(selectorLabels map[string]string, labels map[string]string) bool {
	labelSelector := &metav1.LabelSelector{
		MatchLabels: selectorLabels,
	}

	selector, err := metav1.LabelSelectorAsSelector(labelSelector)
	if err != nil {
		return false
	}

	return selector.Matches(MapLabels(labels))
}
07070100000070000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001D00000000kube-score-1.20.0/score/meta07070100000071000081A4000000000000000000000001680FB1F8000002EB000000000000000000000000000000000000002700000000kube-score-1.20.0/score/meta/labels.gopackage meta

import (
	"regexp"

	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"
)

func Register(allChecks *checks.Checks) {
	allChecks.RegisterMetaCheck("Label values", "Validates label values", validateLabelValues)
}

func validateLabelValues(meta domain.BothMeta) (score scorecard.TestScore, err error) {
	score.Grade = scorecard.GradeAllOK
	r := regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$")
	for key, value := range meta.ObjectMeta.Labels {
		if !r.MatchString(value) {
			score.Grade = scorecard.GradeCritical
			score.AddComment(key, "Invalid label value", "The label value is invalid, and will not be accepted by Kubernetes")
		}
	}
	return
}
07070100000072000081A4000000000000000000000001680FB1F8000003FD000000000000000000000000000000000000002C00000000kube-score-1.20.0/score/meta/labels_test.gopackage meta

import (
	"testing"

	"github.com/stretchr/testify/assert"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
)

func TestInvalidLabel(t *testing.T) {
	t.Parallel()
	s, _ := validateLabelValues(domain.BothMeta{
		ObjectMeta: metav1.ObjectMeta{
			Labels: map[string]string{
				"foo": "engineering/kustomize", // label values can't contain slashes
				"bar": "baribar",
			},
		},
	})
	assert.Equal(t, scorecard.GradeCritical, s.Grade)
	assert.Len(t, s.Comments, 1)
	assert.Equal(t, "foo", s.Comments[0].Path)
	assert.Equal(t, "Invalid label value", s.Comments[0].Summary)
	assert.Equal(t, "The label value is invalid, and will not be accepted by Kubernetes", s.Comments[0].Description)
}
func TestOKLabel(t *testing.T) {
	t.Parallel()
	s, _ := validateLabelValues(domain.BothMeta{
		ObjectMeta: metav1.ObjectMeta{
			Labels: map[string]string{
				"foo": "foo-bar",
			},
		},
	})
	assert.Equal(t, scorecard.GradeAllOK, s.Grade)
}
07070100000073000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002600000000kube-score-1.20.0/score/networkpolicy07070100000074000081A4000000000000000000000001680FB1F80000137E000000000000000000000000000000000000003700000000kube-score-1.20.0/score/networkpolicy/networkpolicy.gopackage networkpolicy

import (
	networkingv1 "k8s.io/api/networking/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/score/internal"
	"github.com/zegl/kube-score/scorecard"
)

func Register(allChecks *checks.Checks, netpols ks.NetworkPolicies, pods ks.Pods, podspecers ks.PodSpeccers) {
	allChecks.RegisterPodCheck("Pod NetworkPolicy", `Makes sure that all Pods are targeted by a NetworkPolicy`, podHasNetworkPolicy(netpols.NetworkPolicies()))
	allChecks.RegisterNetworkPolicyCheck("NetworkPolicy targets Pod", `Makes sure that all NetworkPolicies targets at least one Pod`, networkPolicyTargetsPod(pods.Pods(), podspecers.PodSpeccers()))
}

// podHasNetworkPolicy returns a function that tests that all pods have matching NetworkPolicies
// podHasNetworkPolicy takes a list of all defined NetworkPolicies as input
func podHasNetworkPolicy(allNetpols []ks.NetworkPolicy) func(ks.PodSpecer) (scorecard.TestScore, error) {
	return func(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
		hasMatchingEgressNetpol := false
		hasMatchingIngressNetpol := false

		for _, n := range allNetpols {
			netPol := n.NetworkPolicy()

			// Make sure that the pod and networkpolicy is in the same namespace
			if ps.GetPodTemplateSpec().Namespace != netPol.Namespace {
				continue
			}

			if selector, err := metav1.LabelSelectorAsSelector(&netPol.Spec.PodSelector); err == nil {
				if selector.Matches(internal.MapLabels(ps.GetPodTemplateSpec().Labels)) {

					// Documentation of PolicyTypes
					//
					// List of rule types that the NetworkPolicy relates to.
					// Valid options are "Ingress", "Egress", or "Ingress,Egress".
					// If this field is not specified, it will default based on the existence of Ingress or Egress rules;
					// policies that contain an Egress section are assumed to affect Egress, and all policies
					// (whether or not they contain an Ingress section) are assumed to affect Ingress.
					// If you want to write an egress-only policy, you must explicitly specify policyTypes [ "Egress" ].
					// Likewise, if you want to write a policy that specifies that no egress is allowed,
					// you must specify a policyTypes value that include "Egress" (since such a policy would not include
					// an Egress section and would otherwise default to just [ "Ingress" ]).

					if netPol.Spec.PolicyTypes == nil || len(netPol.Spec.PolicyTypes) == 0 {
						hasMatchingIngressNetpol = true
						if len(netPol.Spec.Egress) > 0 {
							hasMatchingEgressNetpol = true
						}
					} else {
						for _, policyType := range netPol.Spec.PolicyTypes {
							if policyType == networkingv1.PolicyTypeIngress {
								hasMatchingIngressNetpol = true
							}
							if policyType == networkingv1.PolicyTypeEgress {
								hasMatchingEgressNetpol = true
							}
						}
					}
				}
			}
		}

		switch {
		case hasMatchingEgressNetpol && hasMatchingIngressNetpol:
			score.Grade = scorecard.GradeAllOK
		case hasMatchingEgressNetpol && !hasMatchingIngressNetpol:
			score.Grade = scorecard.GradeWarning
			score.AddComment("", "The pod does not have a matching ingress NetworkPolicy", "Add a ingress policy to the pods NetworkPolicy")
		case hasMatchingIngressNetpol && !hasMatchingEgressNetpol:
			score.Grade = scorecard.GradeWarning
			score.AddComment("", "The pod does not have a matching egress NetworkPolicy", "Add a egress policy to the pods NetworkPolicy")
		default:
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "The pod does not have a matching NetworkPolicy", "Create a NetworkPolicy that targets this pod to control who/what can communicate with this pod. Note, this feature needs to be supported by the CNI implementation used in the Kubernetes cluster to have an effect.")
		}

		return
	}
}

func networkPolicyTargetsPod(pods []ks.Pod, podspecers []ks.PodSpecer) func(networkingv1.NetworkPolicy) (scorecard.TestScore, error) {
	return func(netpol networkingv1.NetworkPolicy) (score scorecard.TestScore, err error) {
		hasMatch := false

		for _, p := range pods {
			pod := p.Pod()
			if pod.Namespace != netpol.Namespace {
				continue
			}

			if selector, err := metav1.LabelSelectorAsSelector(&netpol.Spec.PodSelector); err == nil {
				if selector.Matches(internal.MapLabels(pod.Labels)) {
					hasMatch = true
					break
				}
			}
		}

		if !hasMatch {
			for _, pod := range podspecers {
				if pod.GetObjectMeta().Namespace != netpol.Namespace {
					continue
				}

				if selector, err := metav1.LabelSelectorAsSelector(&netpol.Spec.PodSelector); err == nil {
					if selector.Matches(internal.MapLabels(pod.GetPodTemplateSpec().Labels)) {
						hasMatch = true
						break
					}
				}
			}
		}

		if hasMatch {
			score.Grade = scorecard.GradeAllOK
		} else {
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "The NetworkPolicys selector doesn't match any pods", "")
		}

		return
	}
}
07070100000075000081A4000000000000000000000001680FB1F800000DDA000000000000000000000000000000000000003C00000000kube-score-1.20.0/score/networkpolicy/networkpolicy_test.gopackage networkpolicy

import (
	"testing"

	"github.com/stretchr/testify/assert"
	corev1 "k8s.io/api/core/v1"
	v1 "k8s.io/api/networking/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
)

func TestPodHasNetworkPolicy(t *testing.T) {
	t.Parallel()
	cases := []struct {
		polTypes    []v1.PolicyType
		expected    scorecard.Grade
		ingress     []v1.NetworkPolicyIngressRule
		egress      []v1.NetworkPolicyEgressRule
		selectorVal string
	}{
		{
			polTypes:    []v1.PolicyType{v1.PolicyTypeIngress},
			expected:    scorecard.GradeWarning, // has no egress
			selectorVal: "test-a",
		},
		{
			polTypes:    []v1.PolicyType{v1.PolicyTypeEgress},
			expected:    scorecard.GradeWarning, // has no ingress
			selectorVal: "test-a",
		},
		{
			polTypes:    []v1.PolicyType{v1.PolicyTypeEgress, v1.PolicyTypeIngress},
			expected:    scorecard.GradeAllOK,
			selectorVal: "test-a",
		},
		{
			polTypes:    []v1.PolicyType{},
			expected:    scorecard.GradeWarning, // has no egress
			selectorVal: "test-a",
		},
		{
			polTypes:    nil,
			expected:    scorecard.GradeWarning, // has no egress
			selectorVal: "test-a",
		},
		{
			polTypes:    []v1.PolicyType{},
			egress:      []v1.NetworkPolicyEgressRule{{}, {}},
			expected:    scorecard.GradeAllOK,
			selectorVal: "test-a",
		},
		{
			polTypes:    []v1.PolicyType{},
			ingress:     []v1.NetworkPolicyIngressRule{{}, {}},
			expected:    scorecard.GradeWarning, // has no ingress
			selectorVal: "test-a",
		},
		{
			polTypes:    []v1.PolicyType{},
			ingress:     []v1.NetworkPolicyIngressRule{{}, {}},
			egress:      []v1.NetworkPolicyEgressRule{{}, {}},
			expected:    scorecard.GradeAllOK,
			selectorVal: "test-a",
		},
		{
			polTypes:    nil,
			ingress:     []v1.NetworkPolicyIngressRule{{}, {}},
			egress:      []v1.NetworkPolicyEgressRule{{}, {}},
			expected:    scorecard.GradeAllOK,
			selectorVal: "test-a",
		},
		{
			polTypes:    []v1.PolicyType{v1.PolicyTypeEgress, v1.PolicyTypeIngress},
			expected:    scorecard.GradeCritical, // pod has no ingress matching
			selectorVal: "test-not-matching",
		},
	}

	for caseID, tc := range cases {
		pol := v1.NetworkPolicy{
			Spec: v1.NetworkPolicySpec{
				PodSelector: metav1.LabelSelector{
					MatchLabels: map[string]string{"test": tc.selectorVal},
				},
				Ingress:     tc.ingress,
				Egress:      tc.egress,
				PolicyTypes: tc.polTypes,
			},
		}

		pod := corev1.Pod{
			TypeMeta: metav1.TypeMeta{},
			ObjectMeta: metav1.ObjectMeta{
				Labels: map[string]string{
					"test": "test-a",
				},
			},
			Spec: corev1.PodSpec{},
		}

		fn := podHasNetworkPolicy([]domain.NetworkPolicy{np{Obj: pol}})
		spec := corev1.PodTemplateSpec{ObjectMeta: pod.ObjectMeta, Spec: pod.Spec}
		score, _ := fn(&podSpeccer{spec: spec})
		assert.Equal(t, tc.expected, score.Grade, "caseID = %d", caseID)
	}
}

type np struct {
	Obj v1.NetworkPolicy
}

func (n np) NetworkPolicy() v1.NetworkPolicy {
	return n.Obj
}

func (np) FileLocation() domain.FileLocation {
	return domain.FileLocation{}
}

type podSpeccer struct {
	typeMeta   metav1.TypeMeta
	objectMeta metav1.ObjectMeta
	spec       corev1.PodTemplateSpec
}

func (p *podSpeccer) GetTypeMeta() metav1.TypeMeta {
	return p.typeMeta
}

func (p *podSpeccer) GetObjectMeta() metav1.ObjectMeta {
	return p.objectMeta
}

func (p *podSpeccer) GetPodTemplateSpec() corev1.PodTemplateSpec {
	return p.spec
}

func (p *podSpeccer) FileLocation() domain.FileLocation {
	return domain.FileLocation{}
}
07070100000076000081A4000000000000000000000001680FB1F800000E0D000000000000000000000000000000000000002E00000000kube-score-1.20.0/score/networkpolicy_test.gopackage score

import (
	"testing"

	"github.com/zegl/kube-score/scorecard"
)

func TestPodHasNoMatchingNetworkPolicy(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-not-matching.yaml", "Pod NetworkPolicy", scorecard.GradeCritical)
}

func TestPodHasMatchingNetworkPolicy(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-matching.yaml", "Pod NetworkPolicy", scorecard.GradeAllOK)
}

func TestPodHasMatchingIngressNetworkPolicy(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-matching-only-ingress.yaml", "Pod NetworkPolicy", scorecard.GradeWarning)
}

func TestPodHasMatchingEgressNetworkPolicy(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-matching-only-egress.yaml", "Pod NetworkPolicy", scorecard.GradeWarning)
}

func TestNetworkPolicyTargetsPod(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-targets-pod.yaml", "NetworkPolicy targets Pod", scorecard.GradeAllOK)
}

func TestNetworkPolicyTargetsPodInDeployment(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-targets-pod-deployment.yaml", "NetworkPolicy targets Pod", scorecard.GradeAllOK)
}

func TestNetworkPolicyTargetsPodNotMatching(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-targets-pod-not-matching.yaml", "NetworkPolicy targets Pod", scorecard.GradeCritical)
}

func TestNetworkPolicyDeploymentNamespaceMatching(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-deployment-matching.yaml", "NetworkPolicy targets Pod", scorecard.GradeAllOK)
	testExpectedScore(t, "networkpolicy-deployment-matching.yaml", "Pod NetworkPolicy", scorecard.GradeAllOK)
}

func TestNetworkPolicyStatefulSetNamespaceMatching(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-statefulset-matching.yaml", "NetworkPolicy targets Pod", scorecard.GradeAllOK)
	testExpectedScore(t, "networkpolicy-statefulset-matching.yaml", "Pod NetworkPolicy", scorecard.GradeAllOK)
}

func TestNetworkPolicyCronJobNamespaceMatching(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-cronjob-matching.yaml", "NetworkPolicy targets Pod", scorecard.GradeAllOK)
	testExpectedScore(t, "networkpolicy-cronjob-matching.yaml", "Pod NetworkPolicy", scorecard.GradeAllOK)
}

func TestNetworkPolicyDeploymentNamespaceNotMatchingSelector(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-deployment-not-matching-selector.yaml", "NetworkPolicy targets Pod", scorecard.GradeCritical)
	testExpectedScore(t, "networkpolicy-deployment-not-matching-selector.yaml", "Pod NetworkPolicy", scorecard.GradeCritical)
}

func TestNetworkPolicyStatefulSetNamespaceNotMatchingSelector(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-statefulset-not-matching-selector.yaml", "NetworkPolicy targets Pod", scorecard.GradeCritical)
	testExpectedScore(t, "networkpolicy-statefulset-not-matching-selector.yaml", "Pod NetworkPolicy", scorecard.GradeCritical)
}

func TestNetworkPolicyCronJobNamespaceNotMatchingSelector(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-cronjob-not-matching-selector.yaml", "NetworkPolicy targets Pod", scorecard.GradeCritical)
	testExpectedScore(t, "networkpolicy-cronjob-not-matching-selector.yaml", "Pod NetworkPolicy", scorecard.GradeCritical)
}

func TestNetworkPolicyEmptyPodSelector(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "networkpolicy-targets-all-pods.yaml", "NetworkPolicy targets Pod", scorecard.GradeAllOK)
	testExpectedScore(t, "networkpolicy-targets-all-pods.yaml", "Pod NetworkPolicy", scorecard.GradeAllOK)
}
07070100000077000081A4000000000000000000000001680FB1F80000078F000000000000000000000000000000000000002900000000kube-score-1.20.0/score/optional_test.gopackage score

import (
	"testing"

	"github.com/zegl/kube-score/config"
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"
)

func TestOptionalSkippedByDefault(t *testing.T) {
	t.Parallel()
	enabledOptionalTests := make(map[string]struct{})
	wasSkipped(t, []ks.NamedReader{testFile("pod-container-memory-requests.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: enabledOptionalTests,
	}, "Container Memory Requests Equal Limits")
}

func TestOptionalIgnoredAndEnabled(t *testing.T) {
	t.Parallel()

	enabledOptionalTests := make(map[string]struct{})
	enabledOptionalTests["container-resource-requests-equal-limits"] = struct{}{}

	ignoredTests := make(map[string]struct{})
	ignoredTests["container-resource-requests-equal-limits"] = struct{}{}

	wasSkipped(t, []ks.NamedReader{testFile("pod-container-memory-requests.yaml")}, &checks.Config{
		IgnoredTests: ignoredTests,
	}, &config.RunConfiguration{
		EnabledOptionalTests: enabledOptionalTests,
	}, "Container Memory Requests Equal Limits")
}

func TestOptionalRunCliFlagEnabledDefault(t *testing.T) {
	t.Parallel()

	enabledOptionalTests := make(map[string]struct{})
	enabledOptionalTests["container-resource-requests-equal-limits"] = struct{}{}

	testExpectedScoreWithConfig(t,
		[]ks.NamedReader{testFile("pod-container-memory-requests.yaml")}, nil, &config.RunConfiguration{
			EnabledOptionalTests: enabledOptionalTests,
		}, "Container Memory Requests Equal Limits", scorecard.GradeCritical)
}

func TestOptionalRunAnnotationEnabled(t *testing.T) {
	t.Parallel()

	enabledOptionalTests := make(map[string]struct{})

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-container-memory-requests-annotation-optional.yaml")}, nil,
		&config.RunConfiguration{
			EnabledOptionalTests: enabledOptionalTests,
		}, "Container Memory Requests Equal Limits", scorecard.GradeCritical)
}
07070100000078000081A4000000000000000000000001680FB1F800000F6B000000000000000000000000000000000000003400000000kube-score-1.20.0/score/poddisruptionbudget_test.gopackage score

import (
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/stretchr/testify/assert"
	"github.com/zegl/kube-score/scorecard"
)

func TestStatefulSetPodDisruptionBudgetMatches(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-poddisruptionbudget-v1beta1-matches.yaml", "StatefulSet has PodDisruptionBudget", scorecard.GradeAllOK)
}

func TestStatefulSetPodDisruptionBudgetExpressionMatches(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-poddisruptionbudget-v1beta1-expression-matches.yaml", "StatefulSet has PodDisruptionBudget", scorecard.GradeAllOK)
}

func TestStatefulSetPodDisruptionBudgetExpressionNoMatch(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-poddisruptionbudget-v1beta1-expression-no-match.yaml", "StatefulSet has PodDisruptionBudget", scorecard.GradeCritical)
}

func TestStatefulSetPodDisruptionBudgetNoMatch(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-poddisruptionbudget-v1beta1-no-match.yaml", "StatefulSet has PodDisruptionBudget", scorecard.GradeCritical)
}

func TestDeploymentPodDisruptionBudgetMatches(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-poddisruptionbudget-v1beta1-matches.yaml", "Deployment has PodDisruptionBudget", scorecard.GradeAllOK)
}

func TestDeploymentPodDisruptionBudgetExpressionMatches(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-poddisruptionbudget-v1beta1-expression-matches.yaml", "Deployment has PodDisruptionBudget", scorecard.GradeAllOK)
}

func TestDeploymentPodDisruptionBudgetExpressionNoMatch(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-poddisruptionbudget-v1beta1-expression-no-match.yaml", "Deployment has PodDisruptionBudget", scorecard.GradeCritical)
}

func TestDeploymentPodDisruptionBudgetNoMatch(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-poddisruptionbudget-v1beta1-no-match.yaml", "Deployment has PodDisruptionBudget", scorecard.GradeCritical)
}

func TestDeploymentPodDisruptionBudgetNoPolicy(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-poddisruptionbudget-v1beta1-no-policy.yaml", "PodDisruptionBudget has policy", scorecard.GradeCritical)
}

func TestDeploymentPodDisruptionBudgetV1NoPolicy(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-poddisruptionbudget-v1-no-policy.yaml", "PodDisruptionBudget has policy", scorecard.GradeCritical)
}

func TestDeploymentPodDisruptionBudgetV1Matches(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-poddisruptionbudget-v1-matches.yaml", "Deployment has PodDisruptionBudget", scorecard.GradeAllOK)
}

func TestDeploymentPodDisruptionBudgetV1NoMatch(t *testing.T) {
	t.Parallel()
	actual := testExpectedScore(t, "deployment-poddisruptionbudget-v1-no-match.yaml", "Deployment has PodDisruptionBudget", scorecard.GradeCritical)

	expected := []scorecard.TestScoreComment{
		{
			Path:        "",
			Summary:     "No matching PodDisruptionBudget was found",
			Description: "It's recommended to define a PodDisruptionBudget to avoid unexpected downtime during Kubernetes maintenance operations, such as when draining a node. ",
		},
	}

	diff := cmp.Diff(expected, actual)
	assert.Empty(t, diff)
}

func TestDeploymentPodDisruptionBudgetV1NoMatchMatchInOtherNamespace(t *testing.T) {
	t.Parallel()
	actual := testExpectedScore(t, "deployment-poddisruptionbudget-v1-different-namespace.yaml", "Deployment has PodDisruptionBudget", scorecard.GradeCritical)

	expected := []scorecard.TestScoreComment{
		{
			Path:        "",
			Summary:     "No matching PodDisruptionBudget was found",
			Description: "It's recommended to define a PodDisruptionBudget to avoid unexpected downtime during Kubernetes maintenance operations, such as when draining a node. A matching budget was found, but in a different namespace. expected='foo' got='[not-foo bar]'",
		},
	}

	diff := cmp.Diff(expected, actual)
	assert.Empty(t, diff)
}
07070100000079000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000003500000000kube-score-1.20.0/score/podtopologyspreadconstraints0707010000007A000081A4000000000000000000000001680FB1F80000083B000000000000000000000000000000000000005500000000kube-score-1.20.0/score/podtopologyspreadconstraints/podtopologyspreadconstraints.gopackage podtopologyspreadconstraints

import (
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"
)

func Register(allChecks *checks.Checks) {
	allChecks.RegisterPodCheck("Pod Topology Spread Constraints", "Pod Topology Spread Constraints", podTopologySpreadConstraints)
}

func podTopologySpreadConstraints(pod ks.PodSpecer) (score scorecard.TestScore, err error) {
	spreads := pod.GetPodTemplateSpec().Spec.TopologySpreadConstraints

	if spreads == nil {
		score.Grade = scorecard.GradeAllOK
		score.AddComment("", "Pod Topology Spread Constraints", "No Pod Topology Spread Constraints set, kube-scheduler defaults assumed")
		return
	}

	for _, spread := range spreads {
		if spread.LabelSelector == nil {
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "Pod Topology Spread Constraints", "No labelSelector detected. A label selector is needed determine the number of pods in a topology domain")
			return
		}

		if spread.MaxSkew == 0 {
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "Pod Topology Spread Constraints", "MaxSkew is set to zero. This is not allowed.")
			return
		}

		if spread.MinDomains != nil && *spread.MinDomains == 0 {
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "Pod Topology Spread Constraints", "MaxDomain set to zero. This is not allowed. Constraint behaves if minDomains is set to 1 if nil")
			return
		}

		if spread.TopologyKey == "" {
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "Pod Topology Spread Constraints", "TopologyKey is not set. This is the key of node labels used to bucket nodes into a domain")
			return
		}

		if spread.WhenUnsatisfiable != "DoNotSchedule" && spread.WhenUnsatisfiable != "ScheduleAnyway" {
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "Pod Topology Spread Constraints", "Invalid WhenUnsatisfiable setting detected")
			return
		}
	}

	score.Grade = scorecard.GradeAllOK
	score.AddComment("", "Pod Topology Spread Constraints", "Pod Topology Spread Constraints")
	return
}
0707010000007B000081A4000000000000000000000001680FB1F800000672000000000000000000000000000000000000003D00000000kube-score-1.20.0/score/podtopologyspreadconstraints_test.gopackage score

import (
	"testing"

	"github.com/zegl/kube-score/scorecard"
)

func TestPodTopologySpreadContraintsWithOneConstraint(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-topology-spread-constraints-one-constraint.yaml", "Pod Topology Spread Constraints", scorecard.GradeAllOK)
}

func TestPodTopologySpreadContraintsWithTwoConstraints(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-topology-spread-constraints-two-constraints.yaml", "Pod Topology Spread Constraints", scorecard.GradeAllOK)
}

func TestPodTopologySpreadContraintsNoLabelSelector(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-topology-spread-constraints-no-labelselector.yaml", "Pod Topology Spread Constraints", scorecard.GradeCritical)
}

func TestPodTopologySpreadContraintsInvalidMaxSkew(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-topology-spread-constraints-invalid-maxskew.yaml", "Pod Topology Spread Constraints", scorecard.GradeCritical)
}

func TestPodTopologySpreadContraintsInvalidMinDomains(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-topology-spread-constraints-invalid-mindomains.yaml", "Pod Topology Spread Constraints", scorecard.GradeCritical)
}

func TestPodTopologySpreadContraintsNoTopologyKey(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-topology-spread-constraints-no-topologykey.yaml", "Pod Topology Spread Constraints", scorecard.GradeCritical)
}

func TestPodTopologySpreadContraintsInvalidDirective(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-topology-spread-constraints-invalid-whenunsatisfiable.yaml", "Pod Topology Spread Constraints", scorecard.GradeCritical)
}
0707010000007C000081A4000000000000000000000001680FB1F800000F65000000000000000000000000000000000000002600000000kube-score-1.20.0/score/probe_test.gopackage score

import (
	"testing"

	"github.com/stretchr/testify/assert"

	"github.com/zegl/kube-score/scorecard"
)

func TestProbesPodAllMissing(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-all-missing.yaml", "Pod Probes", scorecard.GradeCritical)
	assert.Len(t, comments, 1)
	assert.Equal(t, "Container is missing a readinessProbe", comments[0].Summary)
}

func TestProbesPodMissingReady(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-missing-ready.yaml", "Pod Probes", scorecard.GradeCritical)
	assert.Len(t, comments, 1)
	assert.Equal(t, "Container is missing a readinessProbe", comments[0].Summary)
}

func TestProbesPodIdenticalHTTP(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-identical-http.yaml", "Pod Probes Identical", scorecard.GradeCritical)
	assert.Len(t, comments, 1)
	assert.Equal(t, "Container has the same readiness and liveness probe", comments[0].Summary)
}

func TestProbesPodIdenticalTCP(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-identical-tcp.yaml", "Pod Probes Identical", scorecard.GradeCritical)
	assert.Len(t, comments, 1)
	assert.Equal(t, "Container has the same readiness and liveness probe", comments[0].Summary)
}

func TestProbesPodIdenticalExec(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-identical-exec.yaml", "Pod Probes Identical", scorecard.GradeCritical)
	assert.Len(t, comments, 1)
	assert.Equal(t, "Container has the same readiness and liveness probe", comments[0].Summary)
}

func TestProbesTargetedByService(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-targeted-by-service.yaml", "Pod Probes", scorecard.GradeCritical)
	assert.Len(t, comments, 1)
	assert.Equal(t, "Container is missing a readinessProbe", comments[0].Summary)
}

func TestProbesTargetedByServiceSameNamespace(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-targeted-by-service-same-namespace.yaml", "Pod Probes", scorecard.GradeCritical)
	assert.Len(t, comments, 1)
	assert.Equal(t, "Container is missing a readinessProbe", comments[0].Summary)
}

func TestProbesTargetedByServiceSameNamespaceMultiLabels(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-targeted-by-service-same-namespace-multi-labels.yaml", "Pod Probes", scorecard.GradeCritical)
	assert.Len(t, comments, 1)
	assert.Equal(t, "Container is missing a readinessProbe", comments[0].Summary)
}

func TestProbesTargetedByServiceDifferentNamespace(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-targeted-by-service-different-namespace.yaml", "Pod Probes", scorecard.GradeAllOK)
	assert.Len(t, comments, 1)
	assert.Equal(t, "The pod is not targeted by a service, skipping probe checks.", comments[0].Summary)
}

func TestProbesTargetedByServiceNotTargeted(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-not-targeted-by-service.yaml", "Pod Probes", scorecard.GradeAllOK)
	assert.Len(t, comments, 1)
	assert.Equal(t, "The pod is not targeted by a service, skipping probe checks.", comments[0].Summary)
}

func TestProbesTargetedByServiceNotTargetedMultiLabels(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-not-targeted-by-service-multi-labels.yaml", "Pod Probes", scorecard.GradeAllOK)
	assert.Len(t, comments, 1)
	assert.Equal(t, "The pod is not targeted by a service, skipping probe checks.", comments[0].Summary)
}

func TestProbesMultipleContainers(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-on-different-containers.yaml", "Pod Probes", scorecard.GradeAllOK)
	assert.Len(t, comments, 0)
}

func TestProbesMultipleContainersInit(t *testing.T) {
	t.Parallel()
	comments := testExpectedScore(t, "pod-probes-on-different-containers-init.yaml", "Pod Probes", scorecard.GradeAllOK)
	assert.Len(t, comments, 0)
}
0707010000007D000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001F00000000kube-score-1.20.0/score/probes0707010000007E000081A4000000000000000000000001680FB1F800001875000000000000000000000000000000000000002900000000kube-score-1.20.0/score/probes/probes.gopackage probes

import (
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/score/internal"
	"github.com/zegl/kube-score/scorecard"
	corev1 "k8s.io/api/core/v1"
)

// Register registers the pod checks, including the new one for identical probes.
func Register(allChecks *checks.Checks, services ks.Services) {
	allChecks.RegisterPodCheck("Pod Probes", `Makes sure that all Pods have safe probe configurations`, containerProbes(services.Services()))
	allChecks.RegisterPodCheck("Pod Probes Identical", `Container has the same readiness and liveness probe`, containerProbesIdentical(services.Services()))
}

// containerProbes returns a function that checks if all probes are defined correctly in the Pod.
// Only one probe of each type is required on the entire pod.
// ReadinessProbes are not required if the pod is not targeted by a Service.
//
// containerProbes takes a slice of all defined Services as input.
func containerProbes(allServices []ks.Service) func(ks.PodSpecer) (scorecard.TestScore, error) {
	return func(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
		typeMeta := ps.GetTypeMeta()
		if typeMeta.Kind == "CronJob" && typeMeta.GroupVersionKind().Group == "batch" || typeMeta.Kind == "Job" && typeMeta.GroupVersionKind().Group == "batch" {
			score.Grade = scorecard.GradeAllOK
			return score, nil
		}

		podTemplate := ps.GetPodTemplateSpec()
		allContainers := podTemplate.Spec.InitContainers
		allContainers = append(allContainers, podTemplate.Spec.Containers...)

		hasReadinessProbe := false
		hasLivenessProbe := false
		isTargetedByService := isTargetedByService(allServices, podTemplate)

		// Check probes for each container
		for _, container := range allContainers {
			hasReadinessProbe, hasLivenessProbe = checkBasicProbes(container, hasReadinessProbe, hasLivenessProbe)
		}

		// If pod isn't targeted by a service, skip probe checks
		if !isTargetedByService {
			score.Grade = scorecard.GradeAllOK
			score.Skipped = true
			score.AddComment("", "The pod is not targeted by a service, skipping probe checks.", "")
			return score, nil
		}

		// Evaluate probe checks
		if !hasReadinessProbe {
			score.Grade = scorecard.GradeCritical
			score.AddCommentWithURL("", "Container is missing a readinessProbe",
				"A readinessProbe should be used to indicate when the service is ready to receive traffic. "+
					"Without it, the Pod is risking to receive traffic before it has booted. "+
					"It's also used during rollouts, and can prevent downtime if a new version of the application is failing.",
				"https://github.com/zegl/kube-score/blob/master/README_PROBES.md",
			)
			return score, nil
		}

		if !hasLivenessProbe {
			score.Grade = scorecard.GradeAlmostOK
			score.AddCommentWithURL("", "Container is missing a livenessProbe",
				"A livenessProbe can be used to restart the container if it's deadlocked or has crashed without exiting. "+
					"It's only recommended to setup a livenessProbe if you really need one.",
				"https://github.com/zegl/kube-score/blob/master/README_PROBES.md",
			)
			return score, nil
		}

		score.Grade = scorecard.GradeAllOK
		return score, nil
	}
}

// containerProbesIdentical checks if the container's readiness and liveness probes are identical.
func containerProbesIdentical(allServices []ks.Service) func(ks.PodSpecer) (scorecard.TestScore, error) {
	return func(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
		typeMeta := ps.GetTypeMeta()
		if typeMeta.Kind == "CronJob" && typeMeta.GroupVersionKind().Group == "batch" || typeMeta.Kind == "Job" && typeMeta.GroupVersionKind().Group == "batch" {
			score.Grade = scorecard.GradeAllOK
			return score, nil
		}

		podTemplate := ps.GetPodTemplateSpec()
		allContainers := podTemplate.Spec.InitContainers
		allContainers = append(allContainers, podTemplate.Spec.Containers...)

		probesAreIdentical := false
		for _, container := range allContainers {
			if container.ReadinessProbe != nil && container.LivenessProbe != nil {
				if areProbesIdentical(container.ReadinessProbe, container.LivenessProbe) {
					probesAreIdentical = true
					break
				}
			}
		}

		// If probes are identical, mark it as a critical issue
		if probesAreIdentical {
			score.Grade = scorecard.GradeCritical
			score.AddCommentWithURL(
				"", "Container has the same readiness and liveness probe",
				"Using the same probe for liveness and readiness is very likely dangerous. It's generally better to avoid re-using the same probe.",
				"https://github.com/zegl/kube-score/blob/master/README_PROBES.md",
			)
			return score, nil
		}

		// No identical probes found, return OK grade
		score.Grade = scorecard.GradeAllOK
		return score, nil
	}
}

// areProbesIdentical checks if readiness and liveness probes are identical.
func areProbesIdentical(r, l *corev1.Probe) bool {
	if r.HTTPGet != nil && l.HTTPGet != nil {
		return r.HTTPGet.Path == l.HTTPGet.Path && r.HTTPGet.Port.IntValue() == l.HTTPGet.Port.IntValue()
	}
	if r.TCPSocket != nil && l.TCPSocket != nil {
		return r.TCPSocket.Port == l.TCPSocket.Port
	}
	if r.Exec != nil && l.Exec != nil {
		if len(r.Exec.Command) == len(l.Exec.Command) {
			for i, v := range r.Exec.Command {
				if l.Exec.Command[i] != v {
					return false
				}
			}
			return true
		}
	}
	return false
}

// checkBasicProbes checks for the presence of readiness and liveness probes.
func checkBasicProbes(container corev1.Container, hasReadinessProbe, hasLivenessProbe bool) (bool, bool) {
	if container.ReadinessProbe != nil {
		hasReadinessProbe = true
	}

	if container.LivenessProbe != nil {
		hasLivenessProbe = true
	}

	return hasReadinessProbe, hasLivenessProbe
}

// isTargetedByService checks if the pod is targeted by any of the services.
func isTargetedByService(allServices []ks.Service, podTemplate corev1.PodTemplateSpec) bool {
	for _, s := range allServices {
		if podIsTargetedByService(podTemplate, s.Service()) {
			return true
		}
	}
	return false
}

func podIsTargetedByService(pod corev1.PodTemplateSpec, service corev1.Service) bool {
	if pod.Namespace != service.Namespace {
		return false
	}

	return internal.LabelSelectorMatchesLabels(
		service.Spec.Selector,
		pod.GetObjectMeta().GetLabels(),
	)
}
0707010000007F000081A4000000000000000000000001680FB1F800000A73000000000000000000000000000000000000002E00000000kube-score-1.20.0/score/probes/probes_test.gopackage probes

import (
	"testing"

	"github.com/stretchr/testify/assert"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestPodIsTargetedByService(t *testing.T) {
	t.Run("single label match", func(t *testing.T) {
		res := podIsTargetedByService(v1.PodTemplateSpec{
			ObjectMeta: metav1.ObjectMeta{
				Labels: map[string]string{"foo": "bar"},
			},
		},
			v1.Service{
				Spec: v1.ServiceSpec{
					Selector: map[string]string{"foo": "bar"},
				},
			},
		)

		assert.True(t, res)
	})

	t.Run("single label mismatch", func(t *testing.T) {
		res := podIsTargetedByService(v1.PodTemplateSpec{
			ObjectMeta: metav1.ObjectMeta{
				Labels: map[string]string{"foo": "bar"},
			},
		},
			v1.Service{
				Spec: v1.ServiceSpec{
					Selector: map[string]string{"foo": "baz"},
				},
			},
		)

		assert.False(t, res)
	})

	t.Run("multi label match", func(t *testing.T) {
		res := podIsTargetedByService(v1.PodTemplateSpec{
			ObjectMeta: metav1.ObjectMeta{
				Labels: map[string]string{
					"foo1": "bar1",
					"foo2": "bar2",
				},
			},
		},
			v1.Service{
				Spec: v1.ServiceSpec{
					Selector: map[string]string{
						"foo1": "bar1",
						"foo2": "bar2",
					},
				},
			},
		)

		assert.True(t, res)
	})

	t.Run("multi non full match", func(t *testing.T) {
		res := podIsTargetedByService(v1.PodTemplateSpec{
			ObjectMeta: metav1.ObjectMeta{
				Labels: map[string]string{
					"foo1": "bar1",
					"foo2": "bar2",
				},
			},
		},
			v1.Service{
				Spec: v1.ServiceSpec{
					Selector: map[string]string{
						"foo1": "bar1",
						"foo2": "bar-whatever",
					},
				},
			},
		)

		assert.False(t, res)
	})

	t.Run("multi label match same namespace", func(t *testing.T) {
		res := podIsTargetedByService(v1.PodTemplateSpec{
			ObjectMeta: metav1.ObjectMeta{
				Namespace: "foospace",
				Labels: map[string]string{
					"foo1": "bar1",
					"foo2": "bar2",
				},
			},
		},
			v1.Service{
				ObjectMeta: metav1.ObjectMeta{Namespace: "foospace"},
				Spec: v1.ServiceSpec{
					Selector: map[string]string{
						"foo1": "bar1",
						"foo2": "bar2",
					},
				},
			},
		)

		assert.True(t, res)
	})

	t.Run("multi label match different namespace", func(t *testing.T) {
		res := podIsTargetedByService(v1.PodTemplateSpec{
			ObjectMeta: metav1.ObjectMeta{
				Namespace: "foospace",
				Labels: map[string]string{
					"foo1": "bar1",
					"foo2": "bar2",
				},
			},
		},
			v1.Service{
				ObjectMeta: metav1.ObjectMeta{Namespace: "someOtherNamespace"},
				Spec: v1.ServiceSpec{
					Selector: map[string]string{
						"foo1": "bar1",
						"foo2": "bar2",
					},
				},
			},
		)

		assert.False(t, res)
	})
}
07070100000080000081A4000000000000000000000001680FB1F800001B0B000000000000000000000000000000000000002100000000kube-score-1.20.0/score/score.gopackage score

import (
	"errors"

	"github.com/zegl/kube-score/config"
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/apps"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/score/container"
	"github.com/zegl/kube-score/score/cronjob"
	"github.com/zegl/kube-score/score/deployment"
	"github.com/zegl/kube-score/score/disruptionbudget"
	"github.com/zegl/kube-score/score/hpa"
	"github.com/zegl/kube-score/score/ingress"
	"github.com/zegl/kube-score/score/meta"
	"github.com/zegl/kube-score/score/networkpolicy"
	"github.com/zegl/kube-score/score/podtopologyspreadconstraints"
	"github.com/zegl/kube-score/score/probes"
	"github.com/zegl/kube-score/score/security"
	"github.com/zegl/kube-score/score/service"
	"github.com/zegl/kube-score/score/stable"
	"github.com/zegl/kube-score/scorecard"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func RegisterAllChecks(allObjects ks.AllTypes, checksConfig *checks.Config, runConfig *config.RunConfiguration) *checks.Checks {
	allChecks := checks.New(checksConfig)

	if runConfig == nil {
		runConfig = &config.RunConfiguration{}
	}

	deployment.Register(allChecks, allObjects, runConfig.MinReplicasDeployment)
	ingress.Register(allChecks, allObjects)
	cronjob.Register(allChecks)
	container.Register(allChecks, runConfig.IgnoreContainerCpuLimitRequirement, runConfig.IgnoreContainerMemoryLimitRequirement)
	disruptionbudget.Register(allChecks, allObjects)
	networkpolicy.Register(allChecks, allObjects, allObjects, allObjects)
	probes.Register(allChecks, allObjects)
	security.Register(allChecks)
	service.Register(allChecks, allObjects, allObjects)
	stable.Register(runConfig.KubernetesVersion, allChecks)
	apps.Register(allChecks, allObjects.HorizontalPodAutoscalers(), allObjects.Services())
	meta.Register(allChecks)
	hpa.Register(allChecks, allObjects.Metas(), runConfig.MinReplicasHPA)
	podtopologyspreadconstraints.Register(allChecks)

	return allChecks
}

type podSpeccer struct {
	typeMeta   metav1.TypeMeta
	objectMeta metav1.ObjectMeta
	spec       corev1.PodTemplateSpec
}

func (p *podSpeccer) GetTypeMeta() metav1.TypeMeta {
	return p.typeMeta
}

func (p *podSpeccer) GetObjectMeta() metav1.ObjectMeta {
	return p.objectMeta
}

func (p *podSpeccer) GetPodTemplateSpec() corev1.PodTemplateSpec {
	return p.spec
}

func (p *podSpeccer) FileLocation() ks.FileLocation {
	return ks.FileLocation{}
}

// Score runs a pre-configured list of tests against the files defined in the configuration, and returns a scorecard.
// Additional configuration and tuning parameters can be provided via the config.
func Score(allObjects ks.AllTypes, allChecks *checks.Checks, cnf *config.RunConfiguration) (*scorecard.Scorecard, error) {
	if cnf == nil {
		cnf = &config.RunConfiguration{}
	}

	if allChecks == nil {
		return nil, errors.New("no checks registered")
	}

	scoreCard := scorecard.New()

	newObject := func(typeMeta metav1.TypeMeta, objectMeta metav1.ObjectMeta) *scorecard.ScoredObject {
		return scoreCard.NewObject(typeMeta, objectMeta, cnf)
	}

	for _, ingress := range allObjects.Ingresses() {
		o := newObject(ingress.GetTypeMeta(), ingress.GetObjectMeta())
		for _, test := range allChecks.Ingresses() {
			fn, err := test.Fn(ingress)
			if err != nil {
				return nil, err
			}
			o.Add(fn, test.Check, ingress, ingress.GetObjectMeta().Annotations)
		}
	}

	for _, meta := range allObjects.Metas() {
		o := newObject(meta.TypeMeta, meta.ObjectMeta)
		for _, test := range allChecks.Metas() {
			fn, err := test.Fn(meta)
			if err != nil {
				return nil, err
			}
			o.Add(fn, test.Check, meta, meta.ObjectMeta.Annotations)
		}
	}

	for _, pod := range allObjects.Pods() {
		o := newObject(pod.Pod().TypeMeta, pod.Pod().ObjectMeta)
		for _, test := range allChecks.Pods() {

			podTemplateSpec := corev1.PodTemplateSpec{
				ObjectMeta: pod.Pod().ObjectMeta,
				Spec:       pod.Pod().Spec,
			}

			score, _ := test.Fn(&podSpeccer{
				typeMeta:   pod.Pod().TypeMeta,
				objectMeta: pod.Pod().ObjectMeta,
				spec:       podTemplateSpec,
			})
			o.Add(score, test.Check, pod, pod.Pod().ObjectMeta.Annotations)
		}
	}

	for _, podspecer := range allObjects.PodSpeccers() {
		o := newObject(podspecer.GetTypeMeta(), podspecer.GetObjectMeta())
		for _, test := range allChecks.Pods() {
			score, _ := test.Fn(podspecer)
			o.Add(score, test.Check, podspecer,
				podspecer.GetObjectMeta().Annotations,
				podspecer.GetPodTemplateSpec().Annotations,
			)
		}
	}

	for _, service := range allObjects.Services() {
		o := newObject(service.Service().TypeMeta, service.Service().ObjectMeta)
		for _, test := range allChecks.Services() {
			fn, err := test.Fn(service.Service())
			if err != nil {
				return nil, err
			}
			o.Add(fn, test.Check, service, service.Service().Annotations)
		}
	}

	for _, statefulset := range allObjects.StatefulSets() {
		o := newObject(statefulset.StatefulSet().TypeMeta, statefulset.StatefulSet().ObjectMeta)
		for _, test := range allChecks.StatefulSets() {
			fn, err := test.Fn(statefulset.StatefulSet())
			if err != nil {
				return nil, err
			}
			o.Add(fn, test.Check, statefulset, statefulset.StatefulSet().ObjectMeta.Annotations)
		}
	}

	for _, deployment := range allObjects.Deployments() {
		o := newObject(deployment.Deployment().TypeMeta, deployment.Deployment().ObjectMeta)
		for _, test := range allChecks.Deployments() {
			res, err := test.Fn(deployment.Deployment())
			if err != nil {
				return nil, err
			}
			o.Add(res, test.Check, deployment, deployment.Deployment().ObjectMeta.Annotations)
		}
	}

	for _, netpol := range allObjects.NetworkPolicies() {
		o := newObject(netpol.NetworkPolicy().TypeMeta, netpol.NetworkPolicy().ObjectMeta)
		for _, test := range allChecks.NetworkPolicies() {
			fn, err := test.Fn(netpol.NetworkPolicy())
			if err != nil {
				return nil, err
			}
			o.Add(fn, test.Check, netpol, netpol.NetworkPolicy().ObjectMeta.Annotations)
		}
	}

	for _, cjob := range allObjects.CronJobs() {
		o := newObject(cjob.GetTypeMeta(), cjob.GetObjectMeta())
		for _, test := range allChecks.CronJobs() {
			fn, err := test.Fn(cjob)
			if err != nil {
				return nil, err
			}
			o.Add(fn, test.Check, cjob, cjob.GetObjectMeta().Annotations)
		}
	}

	for _, hpa := range allObjects.HorizontalPodAutoscalers() {
		o := newObject(hpa.GetTypeMeta(), hpa.GetObjectMeta())
		for _, test := range allChecks.HorizontalPodAutoscalers() {
			fn, err := test.Fn(hpa)
			if err != nil {
				return nil, err
			}
			o.Add(fn, test.Check, hpa, hpa.GetObjectMeta().Annotations)
		}
	}

	for _, pdb := range allObjects.PodDisruptionBudgets() {
		o := newObject(pdb.GetTypeMeta(), pdb.GetObjectMeta())
		for _, test := range allChecks.PodDisruptionBudgets() {
			fn, err := test.Fn(pdb)
			if err != nil {
				return nil, err
			}
			o.Add(fn, test.Check, pdb, pdb.GetObjectMeta().Annotations)
		}
	}

	return &scoreCard, nil
}
07070100000081000081A4000000000000000000000001680FB1F800004766000000000000000000000000000000000000002600000000kube-score-1.20.0/score/score_test.gopackage score

import (
	"os"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/stretchr/testify/assert"
	"github.com/zegl/kube-score/config"
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/parser"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"
)

func testFile(name string) *os.File {
	fp, err := os.Open("testdata/" + name)
	if err != nil {
		panic(err)
	}
	return fp
}

// testExpectedScoreWithConfig runs all tests, but makes sure that the test for "testcase" was executed, and that
// the grade is set to expectedScore. The function returns the comments of "testcase".
func testExpectedScoreWithConfig(t *testing.T, files []ks.NamedReader, checksConfig *checks.Config, runConfig *config.RunConfiguration, testcase string, expectedScore scorecard.Grade) []scorecard.TestScoreComment {
	sc, err := testScore(files, checksConfig, runConfig)
	assert.NoError(t, err)

	for _, objectScore := range sc {
		for _, s := range objectScore.Checks {
			if s.Check.Name == testcase {
				assert.Equal(t, expectedScore, s.Grade)
				return s.Comments
			}
		}
	}

	t.Error("Was not tested")
	return nil
}

func wasSkipped(t *testing.T, files []ks.NamedReader, checksConfig *checks.Config, runConfig *config.RunConfiguration, testcase string) bool {
	sc, err := testScore(files, checksConfig, runConfig)
	assert.NoError(t, err)
	for _, objectScore := range sc {
		for _, s := range objectScore.Checks {
			if s.Check.Name == testcase {
				return s.Skipped
			}
		}
	}

	assert.Fail(t, "test was not run")
	return false
}

func getSummaries(t *testing.T, files []ks.NamedReader, checksConfig *checks.Config, runConfig *config.RunConfiguration, testcase string) []string {
	sc, err := testScore(files, checksConfig, runConfig)
	assert.NoError(t, err)
	var summaries []string
	for _, objectScore := range sc {
		for _, s := range objectScore.Checks {
			if s.Check.Name == testcase {
				for _, c := range s.Comments {
					summaries = append(summaries, c.Summary)
				}
				return summaries
			}
		}
	}

	assert.Fail(t, "test was not run")
	return summaries
}

func testScore(files []ks.NamedReader, checksConfig *checks.Config, runConfig *config.RunConfiguration) (scorecard.Scorecard, error) {
	p, err := parser.New(nil)
	if err != nil {
		return nil, err
	}

	parsed, err := p.ParseFiles(files)
	if err != nil {
		return nil, err
	}

	if checksConfig == nil {
		checksConfig = &checks.Config{}
	}
	if runConfig == nil {
		runConfig = &config.RunConfiguration{}
	}

	allChecks := RegisterAllChecks(parsed, checksConfig, runConfig)

	card, err := Score(parsed, allChecks, runConfig)
	if err != nil {
		return nil, err
	}

	return *card, err
}

func testExpectedScore(t *testing.T, filename string, testcase string, expectedScore scorecard.Grade) []scorecard.TestScoreComment {
	return testExpectedScoreWithConfig(t, []ks.NamedReader{testFile(filename)}, nil, &config.RunConfiguration{
		KubernetesVersion: config.Semver{Major: 1, Minor: 18},
	}, testcase, expectedScore)
}

func TestPodContainerNoResources(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-test-resources-none.yaml", "Container Resources", scorecard.GradeCritical)
}

func TestPodContainerResourceLimits(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-test-resources-only-limits.yaml", "Container Resources", scorecard.GradeWarning)
}

func TestPodContainerResourceLimitsAndRequests(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-test-resources-limits-and-requests.yaml", "Container Resources", scorecard.GradeAllOK)
}

func TestPodContainerResourceLimitCpuNotRequired(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-limits-and-requests-no-cpu-limit.yaml")}, nil, &config.RunConfiguration{
		IgnoreContainerCpuLimitRequirement: true,
	}, "Container Resources", scorecard.GradeAllOK)
}

func TestPodContainerResourceLimitCpuRequired(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-limits-and-requests-no-cpu-limit.yaml")}, nil, &config.RunConfiguration{
		IgnoreContainerCpuLimitRequirement: false,
	}, "Container Resources", scorecard.GradeCritical)
}

func TestPodContainerResourceNoLimitRequired(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-no-limits.yaml")}, nil, &config.RunConfiguration{
		IgnoreContainerCpuLimitRequirement:    true,
		IgnoreContainerMemoryLimitRequirement: true,
	}, "Container Resources", scorecard.GradeAllOK)
}

func TestPodContainerResourceRequestsEqualLimits(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-resource-requests-equal-limits"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-limits-and-requests.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Container Resource Requests Equal Limits", scorecard.GradeAllOK)
}

func TestPodContainerMemoryRequestsEqualLimits(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-memory-requests-equal-limits"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-limits-and-requests.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: structMap,
	}, "Container Memory Requests Equal Limits", scorecard.GradeAllOK)
}

func TestPodContainerCPURequestsEqualLimits(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-cpu-requests-equal-limits"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-limits-and-requests.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: structMap,
	}, "Container CPU Requests Equal Limits", scorecard.GradeAllOK)
}

func TestPodContainerResourceRequestsEqualLimitsNoLimits(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-resource-requests-equal-limits"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-no-limits.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Container Resource Requests Equal Limits", scorecard.GradeCritical)
}

func TestPodContainerResourceRequestsEqualLimitsNoLimitsAnnotation(t *testing.T) {
	t.Parallel()

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-no-limits-annotation-optional.yaml")}, nil, &config.RunConfiguration{
		UseOptionalChecksAnnotation: true,
	}, "Container Resource Requests Equal Limits", scorecard.GradeCritical)
}

func TestPodContainerMemoryRequestsEqualLimitsNoLimits(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-memory-requests-equal-limits"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-no-limits.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Container Memory Requests Equal Limits", scorecard.GradeCritical)
}

func TestPodContainerMemoryRequestsEqualLimitsNoLimitsAnnotation(t *testing.T) {
	t.Parallel()

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-no-limits-annotation-optional.yaml")}, nil, &config.RunConfiguration{

		UseOptionalChecksAnnotation: true,
	}, "Container Memory Requests Equal Limits", scorecard.GradeCritical)
}

func TestPodContainerCPURequestsEqualLimitsNoLimits(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-cpu-requests-equal-limits"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-no-limits.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Container CPU Requests Equal Limits", scorecard.GradeCritical)
}

func TestPodContainerCPURequestsEqualLimitsNoLimitsAnnotation(t *testing.T) {
	t.Parallel()

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-test-resources-no-limits-annotation-optional.yaml")}, nil, &config.RunConfiguration{

		UseOptionalChecksAnnotation: true,
	}, "Container CPU Requests Equal Limits", scorecard.GradeCritical)
}

func TestDeploymentResources(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-test-resources.yaml", "Container Resources", scorecard.GradeWarning)
}

func TestStatefulSetResources(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-test-resources.yaml", "Container Resources", scorecard.GradeWarning)
}

func TestPodContainerTagLatest(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-image-tag-latest.yaml", "Container Image Tag", scorecard.GradeCritical)
}

func TestPodContainerTagFixed(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-image-tag-fixed.yaml", "Container Image Tag", scorecard.GradeAllOK)
}

func TestPodContainerPullPolicyUndefined(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-image-pullpolicy-undefined.yaml", "Container Image Pull Policy", scorecard.GradeCritical)
}

func TestPodContainerPullPolicyUndefinedLatestTag(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-image-pullpolicy-undefined-latest-tag.yaml", "Container Image Pull Policy", scorecard.GradeAllOK)
}

func TestPodContainerPullPolicyUndefinedNoTag(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-image-pullpolicy-undefined-no-tag.yaml", "Container Image Pull Policy", scorecard.GradeAllOK)
}

func TestPodContainerPullPolicyNever(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-image-pullpolicy-never.yaml", "Container Image Pull Policy", scorecard.GradeCritical)
}

func TestPodContainerPullPolicyAlways(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-image-pullpolicy-always.yaml", "Container Image Pull Policy", scorecard.GradeAllOK)
}

func TestConfigMapMultiDash(t *testing.T) {
	t.Parallel()
	_, err := testScore([]ks.NamedReader{testFile("configmap-multi-dash.yaml")}, nil, nil)
	assert.Nil(t, err)
}

func TestAnnotationIgnore(t *testing.T) {
	t.Parallel()
	s, err := testScore([]ks.NamedReader{testFile("ignore-annotation-service.yaml")}, nil, &config.RunConfiguration{
		UseIgnoreChecksAnnotation: true,
	})
	assert.Nil(t, err)
	assert.Len(t, s, 1)

	tested := false

	for _, o := range s {
		for _, c := range o.Checks {
			if c.Check.ID == "service-type" {
				assert.True(t, c.Skipped)
				tested = true
			}
		}
		assert.Equal(t, "node-port-service-with-ignore", o.ObjectMeta.Name)
	}
	assert.True(t, tested)
}

func TestAnnotationIgnoreDisabled(t *testing.T) {
	t.Parallel()
	s, err := testScore([]ks.NamedReader{testFile("ignore-annotation-service.yaml")}, nil, &config.RunConfiguration{
		UseIgnoreChecksAnnotation: false,
	})
	assert.Nil(t, err)
	assert.Len(t, s, 1)

	tested := false

	for _, o := range s {
		for _, c := range o.Checks {
			if c.Check.ID == "service-type" {
				assert.False(t, c.Skipped)
				assert.Equal(t, scorecard.GradeWarning, c.Grade)
				tested = true
			}
		}
		assert.Equal(t, "node-port-service-with-ignore", o.ObjectMeta.Name)
	}
	assert.True(t, tested)
}

func TestList(t *testing.T) {
	t.Parallel()
	s, err := testScore([]ks.NamedReader{testFile("list.yaml")}, nil, nil)
	assert.Nil(t, err)
	assert.Len(t, s, 2)

	hasService := false
	hasDeployment := false

	for _, obj := range s {
		if obj.ObjectMeta.Name == "list-service-test" {
			hasService = true
		}
		if obj.ObjectMeta.Name == "list-deployment-test" {
			hasDeployment = true
		}
		assert.Condition(t, func() bool { return len(obj.Checks) > 2 })
	}

	assert.True(t, hasService)
	assert.True(t, hasDeployment)
}

// Note the input file specifies a condition that would fail the optional matching request and limit test, but returns GradeAllOK
// when only the default case is evaluated
func TestPodContainerStorageEphemeralRequestAndLimitOK(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-ephemeral-storage-request-nomatch-limit.yaml", "Container Ephemeral Storage Request and Limit", scorecard.GradeAllOK)
}

func TestPodContainerStorageEphemeralNoLimit(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-ephemeral-storage-missing-limit.yaml", "Container Ephemeral Storage Request and Limit", scorecard.GradeCritical)
}

func TestPodContainerStorageEphemeralNoRequest(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "pod-ephemeral-storage-missing-request.yaml", "Container Ephemeral Storage Request and Limit", scorecard.GradeWarning)
}

func TestPodContainerStorageEphemeralRequestEqualsLimit(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-ephemeral-storage-request-equals-limit"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-ephemeral-storage-request-matches-limit.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Container Ephemeral Storage Request Equals Limit", scorecard.GradeAllOK)
}

func TestPodContainerStorageEphemeralRequestNoMatchLimit(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-ephemeral-storage-request-equals-limit"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-ephemeral-storage-request-nomatch-limit.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: structMap,
	}, "Container Ephemeral Storage Request Equals Limit", scorecard.GradeCritical)
}

func TestPodContainerStorageEphemeralRequestNoMatchLimitAnnotation(t *testing.T) {
	t.Parallel()

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-ephemeral-storage-request-nomatch-limit-annotation-optional.yaml")}, nil, &config.RunConfiguration{

		UseOptionalChecksAnnotation: true,
	}, "Container Ephemeral Storage Request Equals Limit", scorecard.GradeCritical)
}

func TestPodContainerStorageEphemeralIgnoreAnnotation(t *testing.T) {

	t.Parallel()
	s, err := testScore([]ks.NamedReader{testFile("pod-ephemeral-storage-annotation-ignore.yaml")}, nil, &config.RunConfiguration{

		UseIgnoreChecksAnnotation: true,
	})
	assert.Nil(t, err)
	assert.Len(t, s, 1)

	tested := false

	for _, o := range s {
		for _, c := range o.Checks {
			if c.Check.ID == "container-resources" {
				assert.True(t, c.Skipped)
				tested = true
			}
		}
		assert.Equal(t, "pod-ephemeral-storage-annotation-ignore", o.ObjectMeta.Name)
	}
	assert.True(t, tested)
}

func TestPodContainerPortsContainerPortMissing(t *testing.T) {
	t.Parallel()
	structMap := make(map[string]struct{})
	structMap["container-ports-check"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-container-ports-missing-containerport.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Container Ports Check", scorecard.GradeCritical)
}

func TestPodContainerPortsContainerPortMissingAnnotation(t *testing.T) {
	t.Parallel()

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-container-ports-missing-containerport-annotation-optional.yaml")}, nil, &config.RunConfiguration{

		UseOptionalChecksAnnotation: true,
	}, "Container Ports Check", scorecard.GradeCritical)
}

func TestPodContainerPortsDuplicateNames(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-ports-check"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-container-ports-duplicate-names.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Container Ports Check", scorecard.GradeCritical)
}

func TestPodContainerPortsNameLength(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-ports-check"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-container-ports-name-too-long.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Container Ports Check", scorecard.GradeCritical)
}

func TestPodContainerPortsOK(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-ports-check"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-container-ports-ok.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Container Ports Check", scorecard.GradeAllOK)
}

func TestPodEnvOK(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["environment-variable-key-duplication"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-env-ok.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Environment Variable Key Duplication", scorecard.GradeAllOK)
}

func TestPodEnvDuplicated(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["environment-variable-key-duplication"] = struct{}{}

	actual := testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-env-duplicated.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Environment Variable Key Duplication", scorecard.GradeCritical)

	expected := []scorecard.TestScoreComment{
		{
			Path:        "foobar",
			Summary:     "Environment Variable Key Duplication",
			Description: "Container environment variable key 'bar' is duplicated",
		},
		{
			Path:        "foobar",
			Summary:     "Environment Variable Key Duplication",
			Description: "Container environment variable key 'baz' is duplicated",
		},
	}
	diff := cmp.Diff(expected, actual)
	assert.Empty(t, diff)
}

func TestMultipleIgnoreAnnotations(t *testing.T) {
	t.Parallel()
	s, err := testScore([]ks.NamedReader{testFile("kube-score-ignore-annotations.yaml")}, nil, &config.RunConfiguration{
		UseIgnoreChecksAnnotation: true,
	})
	assert.Nil(t, err)
	assert.Len(t, s, 1)

	tested := false
	skipped := false

	for _, o := range s {
		for _, c := range o.Checks {
			// implied by the ignore container-resources annotation
			if c.Check.ID == "container-ephemeral-storage-request-and-limit" {
				assert.True(t, c.Skipped)
				skipped = true
			}
			// a default check
			if c.Check.ID == "container-image-pull-policy" {
				assert.False(t, c.Skipped)
				assert.Equal(t, scorecard.GradeCritical, c.Grade)
				tested = true
			}
			assert.Equal(t, "kube-score-ignore-annotations", o.ObjectMeta.Name)
		}
	}
	assert.True(t, tested)
	assert.True(t, skipped)
}
07070100000082000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002100000000kube-score-1.20.0/score/security07070100000083000081A4000000000000000000000001680FB1F800001A0F000000000000000000000000000000000000002D00000000kube-score-1.20.0/score/security/security.gopackage security

import (
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"
	corev1 "k8s.io/api/core/v1"
)

func Register(allChecks *checks.Checks) {
	allChecks.RegisterPodCheck("Container Security Context User Group ID", `Makes sure that all pods have a security context with valid UID and GID set `, containerSecurityContextUserGroupID)
	allChecks.RegisterPodCheck("Container Security Context Privileged", "Makes sure that all pods have a unprivileged security context set", containerSecurityContextPrivileged)
	allChecks.RegisterPodCheck("Container Security Context ReadOnlyRootFilesystem", "Makes sure that all pods have a security context with read only filesystem set", containerSecurityContextReadOnlyRootFilesystem)

	allChecks.RegisterOptionalPodCheck("Container Seccomp Profile", `Makes sure that all pods have at a seccomp policy configured.`, podSeccompProfile)
}

// containerSecurityContextReadOnlyRootFilesystem checks for pods using writeable root filesystems
func containerSecurityContextReadOnlyRootFilesystem(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	allContainers := ps.GetPodTemplateSpec().Spec.InitContainers
	allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...)

	noContextSet := false
	hasWritableRootFS := false

	for _, container := range allContainers {
		if container.SecurityContext == nil {
			noContextSet = true
			score.AddComment(container.Name, "Container has no configured security context", "Set securityContext to run the container in a more secure context.")
			continue
		}
		sec := container.SecurityContext
		if sec.ReadOnlyRootFilesystem == nil || !*sec.ReadOnlyRootFilesystem {
			hasWritableRootFS = true
			score.AddComment(container.Name, "The pod has a container with a writable root filesystem", "Set securityContext.readOnlyRootFilesystem to true")
		}
	}

	if noContextSet || hasWritableRootFS {
		score.Grade = scorecard.GradeCritical
	} else {
		score.Grade = scorecard.GradeAllOK
	}

	return
}

// containerSecurityContextPrivileged checks for privileged containers
func containerSecurityContextPrivileged(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	allContainers := ps.GetPodTemplateSpec().Spec.InitContainers
	allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...)
	hasPrivileged := false
	for _, container := range allContainers {
		if container.SecurityContext != nil && container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged {
			hasPrivileged = true
			score.AddComment(container.Name, "The container is privileged", "Set securityContext.privileged to false. Privileged containers can access all devices on the host, and grants almost the same access as non-containerized processes on the host.")
		}
	}
	if hasPrivileged {
		score.Grade = scorecard.GradeCritical
	} else {
		score.Grade = scorecard.GradeAllOK
	}
	return
}

// containerSecurityContextUserGroupID checks that the user and group are valid ( > 10000) in the security context
func containerSecurityContextUserGroupID(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	allContainers := ps.GetPodTemplateSpec().Spec.InitContainers
	allContainers = append(allContainers, ps.GetPodTemplateSpec().Spec.Containers...)
	podSecurityContext := ps.GetPodTemplateSpec().Spec.SecurityContext
	noContextSet := false
	hasLowUserID := false
	hasLowGroupID := false
	for _, container := range allContainers {
		if container.SecurityContext == nil && podSecurityContext == nil {
			noContextSet = true
			score.AddComment(container.Name, "Container has no configured security context", "Set securityContext to run the container in a more secure context.")
			continue
		}
		sec := container.SecurityContext
		if sec == nil {
			sec = &corev1.SecurityContext{}
		}
		// Forward values from PodSecurityContext to the (container level) SecurityContext if not set
		if podSecurityContext != nil {
			if sec.RunAsGroup == nil {
				sec.RunAsGroup = podSecurityContext.RunAsGroup
			}
			if sec.RunAsUser == nil {
				sec.RunAsUser = podSecurityContext.RunAsUser
			}
		}
		if sec.RunAsUser == nil || *sec.RunAsUser < 10000 {
			hasLowUserID = true
			score.AddComment(container.Name, "The container is running with a low user ID", "A userid above 10 000 is recommended to avoid conflicts with the host. Set securityContext.runAsUser to a value > 10000")
		}

		if sec.RunAsGroup == nil || *sec.RunAsGroup < 10000 {
			hasLowGroupID = true
			score.AddComment(container.Name, "The container running with a low group ID", "A groupid above 10 000 is recommended to avoid conflicts with the host. Set securityContext.runAsGroup to a value > 10000")
		}
	}
	if noContextSet || hasLowUserID || hasLowGroupID {
		score.Grade = scorecard.GradeCritical
	} else {
		score.Grade = scorecard.GradeAllOK
	}
	return
}

// podSeccompProfile checks that a Seccommp profile is configured. The
// seccompProfile can be specified either through annotation or securityContext.
// There are two ways to specify the seccomp profile via securityContext --
// at the pod level or  container level.
// Pod level seccomp profile is preferred since it is applied to all containers.
func podSeccompProfile(ps ks.PodSpecer) (score scorecard.TestScore, err error) {
	metadata := ps.GetPodTemplateSpec().ObjectMeta

	secured := false

	// Check if the seccomp profile is set via annotation
	if metadata.Annotations != nil {
		if _, ok := metadata.Annotations["seccomp.security.alpha.kubernetes.io/defaultProfileName"]; ok {
			secured = true
		}
	}

	//Check if seccomp is set via securityContext at Pod or Container Level
	if !secured {
		elements := make(map[string]bool)
		if ps.GetPodTemplateSpec().Spec.SecurityContext != nil && ps.GetPodTemplateSpec().Spec.SecurityContext.SeccompProfile != nil {
			secured = true
		} else {
			// This does not check initContainers, only Containers
			for _, container := range ps.GetPodTemplateSpec().Spec.Containers {
				if container.SecurityContext != nil && container.SecurityContext.SeccompProfile != nil {
					elements[container.Name] = true
					secured = true
				} else {
					score.AddComment(container.Name, "The container has not configured Seccomp", "Running containers with Seccomp is recommended to reduce the kernel attack surface")
					elements[container.Name] = false
				}
			}
		}

		// one unsecured container is enough to fail the test
		for _, value := range elements {
			if !value {
				secured = false
			}
		}
	}

	if !secured {
		score.Grade = scorecard.GradeWarning
	} else {
		score.Grade = scorecard.GradeAllOK
	}

	return
}
07070100000084000081A4000000000000000000000001680FB1F8000022D8000000000000000000000000000000000000002900000000kube-score-1.20.0/score/security_test.gopackage score

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/zegl/kube-score/config"
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
)

func TestContainerSeccompMissing(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-seccomp-profile"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-seccomp-no-annotation.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: structMap,
	}, "Container Seccomp Profile", scorecard.GradeWarning)

}

func TestPodSecurityContextPresent(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-seccomp-profile"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-seccomp-securecontext-ok.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: structMap,
	}, "Container Seccomp Profile", scorecard.GradeAllOK)
}

func TestContainerSecurityContextSeccompPresent(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-seccomp-profile"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-seccomp-container-securecontext-ok.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: structMap,
	}, "Container Seccomp Profile", scorecard.GradeAllOK)
}

func TestPodSecurityContextSeccompAbsent(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-seccomp-profile"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-seccomp-securecontext-warning.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: structMap,
	}, "Container Seccomp Profile", scorecard.GradeWarning)
}

func TestContainerSecurityContextSeccompAbsent(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-seccomp-profile"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-seccomp-container-securecontext-warning.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: structMap,
	}, "Container Seccomp Profile", scorecard.GradeWarning)
}

func TestContainerSeccompMissingNotRunByDefault(t *testing.T) {
	t.Parallel()
	skipped := wasSkipped(t, []ks.NamedReader{testFile("pod-seccomp-no-annotation.yaml")}, nil, nil, "Container Seccomp Profile")
	assert.True(t, skipped)
}

func TestContainerSeccompAllGood(t *testing.T) {
	t.Parallel()

	structMap := make(map[string]struct{})
	structMap["container-seccomp-profile"] = struct{}{}

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-seccomp-annotated.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: structMap,
	}, "Container Seccomp Profile", scorecard.GradeAllOK)
}

func TestContainerSeccompAllGoodAnnotation(t *testing.T) {
	t.Parallel()

	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-seccomp-annotated-annotation-optional.yaml")}, nil, &config.RunConfiguration{
		UseOptionalChecksAnnotation: true,
	}, "Container Seccomp Profile", scorecard.GradeAllOK)
}

func TestContainerSecurityContextUserGroupIDAllGood(t *testing.T) {
	t.Parallel()
	structMap := make(map[string]struct{})
	structMap["container-security-context-user-group-id"] = struct{}{}
	c := testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-security-context-all-good.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: structMap,
	}, "Container Security Context User Group ID", scorecard.GradeAllOK)
	assert.Empty(t, c)
}

func TestContainerSecurityContextUserGroupIDLowGroup(t *testing.T) {
	t.Parallel()
	optionalChecks := make(map[string]struct{})
	optionalChecks["container-security-context-user-group-id"] = struct{}{}
	comments := testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-security-context-low-group-id.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: optionalChecks,
	}, "Container Security Context User Group ID", scorecard.GradeCritical)
	assert.Contains(t, comments, scorecard.TestScoreComment{
		Path:        "foobar",
		Summary:     "The container running with a low group ID",
		Description: "A groupid above 10 000 is recommended to avoid conflicts with the host. Set securityContext.runAsGroup to a value > 10000",
	})
}

func TestContainerSecurityContextUserGroupIDLowUser(t *testing.T) {
	t.Parallel()
	optionalChecks := make(map[string]struct{})
	optionalChecks["container-security-context-user-group-id"] = struct{}{}
	comments := testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-security-context-low-user-id.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: optionalChecks,
	}, "Container Security Context User Group ID", scorecard.GradeCritical)
	assert.Contains(t, comments, scorecard.TestScoreComment{
		Path:        "foobar",
		Summary:     "The container is running with a low user ID",
		Description: "A userid above 10 000 is recommended to avoid conflicts with the host. Set securityContext.runAsUser to a value > 10000",
	})
}

func TestContainerSecurityContextUserGroupIDNoSecurityContext(t *testing.T) {
	t.Parallel()
	optionalChecks := make(map[string]struct{})
	optionalChecks["container-security-context-user-group-id"] = struct{}{}
	comments := testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-security-context-nosecuritycontext.yaml")}, nil, &config.RunConfiguration{

		EnabledOptionalTests: optionalChecks,
	}, "Container Security Context User Group ID", scorecard.GradeCritical)
	assert.Contains(t, comments, scorecard.TestScoreComment{
		Path:        "foobar",
		Summary:     "Container has no configured security context",
		Description: "Set securityContext to run the container in a more secure context.",
	})
}

func TestContainerSecurityContextPrivilegedAllGood(t *testing.T) {
	t.Parallel()
	structMap := make(map[string]struct{})
	structMap["container-security-context-privileged"] = struct{}{}
	c := testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-security-context-all-good.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: structMap,
	}, "Container Security Context Privileged", scorecard.GradeAllOK)
	assert.Empty(t, c)
}

func TestContainerSecurityContextPrivilegedPrivileged(t *testing.T) {
	t.Parallel()
	optionalChecks := make(map[string]struct{})
	optionalChecks["container-security-context-privileged"] = struct{}{}
	comments := testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-security-context-privileged.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: optionalChecks,
	}, "Container Security Context Privileged", scorecard.GradeCritical)
	assert.Contains(t, comments, scorecard.TestScoreComment{
		Path:        "foobar",
		Summary:     "The container is privileged",
		Description: "Set securityContext.privileged to false. Privileged containers can access all devices on the host, and grants almost the same access as non-containerized processes on the host.",
	})
}

func TestContainerSecurityContextReadOnlyRootFilesystemAllGood(t *testing.T) {
	t.Parallel()
	structMap := make(map[string]struct{})
	structMap["container-security-context-readonlyrootfilesystem"] = struct{}{}
	c := testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-security-context-all-good.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: structMap,
	}, "Container Security Context ReadOnlyRootFilesystem", scorecard.GradeAllOK)
	assert.Empty(t, c)
}

func TestContainerSecurityContextReadOnlyRootFilesystemWriteable(t *testing.T) {
	t.Parallel()
	optionalChecks := make(map[string]struct{})
	optionalChecks["container-security-context-readonlyrootfilesystem"] = struct{}{}
	comments := testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-security-context-writeablerootfilesystem.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: optionalChecks,
	}, "Container Security Context ReadOnlyRootFilesystem", scorecard.GradeCritical)
	assert.Contains(t, comments, scorecard.TestScoreComment{
		Path:        "foobar",
		Summary:     "The pod has a container with a writable root filesystem",
		Description: "Set securityContext.readOnlyRootFilesystem to true",
	})
}

func TestContainerSecurityContextReadOnlyRootFilesystemNoSecurityContext(t *testing.T) {
	t.Parallel()
	optionalChecks := make(map[string]struct{})
	optionalChecks["container-security-context-readonlyrootfilesystem"] = struct{}{}
	comments := testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("pod-security-context-nosecuritycontext.yaml")}, nil, &config.RunConfiguration{
		EnabledOptionalTests: optionalChecks,
	}, "Container Security Context ReadOnlyRootFilesystem", scorecard.GradeCritical)
	assert.Contains(t, comments, scorecard.TestScoreComment{
		Path:        "foobar",
		Summary:     "Container has no configured security context",
		Description: "Set securityContext to run the container in a more secure context.",
	})
}
07070100000085000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002000000000kube-score-1.20.0/score/service07070100000086000081A4000000000000000000000001680FB1F800000A2B000000000000000000000000000000000000002B00000000kube-score-1.20.0/score/service/service.gopackage service

import (
	corev1 "k8s.io/api/core/v1"

	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/score/internal"
	"github.com/zegl/kube-score/scorecard"
)

func Register(allChecks *checks.Checks, pods ks.Pods, podspeccers ks.PodSpeccers) {
	allChecks.RegisterServiceCheck("Service Targets Pod", `Makes sure that all Services targets a Pod`, serviceTargetsPod(pods.Pods(), podspeccers.PodSpeccers()))
	allChecks.RegisterServiceCheck("Service Type", `Makes sure that the Service type is not NodePort`, serviceType)
}

// serviceTargetsPod checks if a Service targets a pod and issues a critical warning if no matching pod
// could be found
func serviceTargetsPod(pods []ks.Pod, podspecers []ks.PodSpecer) func(corev1.Service) (scorecard.TestScore, error) {
	podsInNamespace := make(map[string][]map[string]string)
	for _, p := range pods {
		pod := p.Pod()
		if _, ok := podsInNamespace[pod.Namespace]; !ok {
			podsInNamespace[pod.Namespace] = []map[string]string{}
		}
		podsInNamespace[pod.Namespace] = append(podsInNamespace[pod.Namespace], pod.Labels)
	}
	for _, podSpec := range podspecers {
		if _, ok := podsInNamespace[podSpec.GetObjectMeta().Namespace]; !ok {
			podsInNamespace[podSpec.GetObjectMeta().Namespace] = []map[string]string{}
		}
		podsInNamespace[podSpec.GetObjectMeta().Namespace] = append(podsInNamespace[podSpec.GetObjectMeta().Namespace], podSpec.GetPodTemplateSpec().Labels)
	}

	return func(service corev1.Service) (score scorecard.TestScore, err error) {
		// Services of type ExternalName does not have a selector
		if service.Spec.Type == corev1.ServiceTypeExternalName {
			score.Grade = scorecard.GradeAllOK
			return
		}

		hasMatch := false

		for _, podLables := range podsInNamespace[service.Namespace] {
			if internal.LabelSelectorMatchesLabels(service.Spec.Selector, podLables) {
				hasMatch = true
				break
			}
		}

		if hasMatch {
			score.Grade = scorecard.GradeAllOK
		} else {
			score.Grade = scorecard.GradeCritical
			score.AddComment("", "The services selector does not match any pods", "")
		}

		return
	}
}

func serviceType(service corev1.Service) (score scorecard.TestScore, err error) {
	if service.Spec.Type == corev1.ServiceTypeNodePort {
		score.Grade = scorecard.GradeWarning
		score.AddComment("", "The service is of type NodePort", "NodePort services should be avoided as they are insecure, and can not be used together with NetworkPolicies. LoadBalancers or use of an Ingress is recommended over NodePorts.")
		return
	}

	score.Grade = scorecard.GradeAllOK
	return
}
07070100000087000081A4000000000000000000000001680FB1F8000009E4000000000000000000000000000000000000002800000000kube-score-1.20.0/score/service_test.gopackage score

import (
	"testing"

	"github.com/zegl/kube-score/scorecard"
)

func TestServiceTargetsPodDeployment(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-target-deployment.yaml", "Service Targets Pod", scorecard.GradeAllOK)
}

func TestServiceNotTargetsPodDeployment(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-not-target-deployment.yaml", "Service Targets Pod", scorecard.GradeCritical)
}

func TestServiceTargetsPodRaw(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-target-pod.yaml", "Service Targets Pod", scorecard.GradeAllOK)
}

func TestServiceNotTargetsPodRaw(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-not-target-pod.yaml", "Service Targets Pod", scorecard.GradeCritical)
}

func TestServiceTargetsPodRawMultiLabel(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-target-pod-multi-label.yaml", "Service Targets Pod", scorecard.GradeAllOK)
}

func TestServiceNotTargetsPodRawMultiLabel(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-not-target-pod-multi-label.yaml", "Service Targets Pod", scorecard.GradeCritical)
}

func TestServiceTargetsPodRawSameNamespace(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-target-pod-same-namespace.yaml", "Service Targets Pod", scorecard.GradeAllOK)
}

func TestServiceTargetsPodRawDifferentNamespace(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-target-pod-different-namespace.yaml", "Service Targets Pod", scorecard.GradeCritical)
}

func TestServiceTargetsPodDeploymentSameNamespace(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-target-deployment-same-namespace.yaml", "Service Targets Pod", scorecard.GradeAllOK)
}

func TestServiceTargetsPodDeploymentDifferentNamespace(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-target-deployment-different-namespace.yaml", "Service Targets Pod", scorecard.GradeCritical)
}

func TestServiceExternalName(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-externalname.yaml", "Service Targets Pod", scorecard.GradeAllOK)
}

func TestServiceTypeNodePort(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-type-nodeport.yaml", "Service Type", scorecard.GradeWarning)
}

func TestServiceTypeClusterIP(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-type-clusterip.yaml", "Service Type", scorecard.GradeAllOK)
}

func TestServiceTypeDefault(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "service-type-default.yaml", "Service Type", scorecard.GradeAllOK)
}
07070100000088000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001F00000000kube-score-1.20.0/score/stable07070100000089000081A4000000000000000000000001680FB1F800000B55000000000000000000000000000000000000003100000000kube-score-1.20.0/score/stable/stable_version.gopackage stable

import (
	"fmt"

	"github.com/zegl/kube-score/config"
	"github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/score/checks"
	"github.com/zegl/kube-score/scorecard"
)

func Register(kubernetesVersion config.Semver, allChecks *checks.Checks) {
	allChecks.RegisterMetaCheck("Stable version", `Checks if the object is using a deprecated apiVersion`, metaStableAvailable(kubernetesVersion))
}

// ScoreMetaStableAvailable checks if the supplied TypeMeta is an unstable object type, that has a stable(r) replacement
func metaStableAvailable(kubernetsVersion config.Semver) func(meta domain.BothMeta) (scorecard.TestScore, error) {
	return func(meta domain.BothMeta) (score scorecard.TestScore, err error) {
		type recommendedApi struct {
			newAPI         string
			availableSince config.Semver
		}

		withStable := map[string]map[string]recommendedApi{
			"extensions/v1beta1": {
				"Deployment":   recommendedApi{"apps/v1", config.Semver{Major: 1, Minor: 9}},
				"DaemonSet":    recommendedApi{"apps/v1", config.Semver{Major: 1, Minor: 9}},
				"Ingress":      recommendedApi{"networking.k8s.io/v1", config.Semver{Major: 1, Minor: 19}},
				"IngressClass": recommendedApi{"networking.k8s.io/v1", config.Semver{Major: 1, Minor: 19}},
			},
			"apps/v1beta1": {
				"Deployment":  recommendedApi{"apps/v1", config.Semver{Major: 1, Minor: 9}},
				"StatefulSet": recommendedApi{"apps/v1", config.Semver{Major: 1, Minor: 9}},
			},
			"apps/v1beta2": {
				"Deployment":  recommendedApi{"apps/v1", config.Semver{Major: 1, Minor: 9}},
				"StatefulSet": recommendedApi{"apps/v1", config.Semver{Major: 1, Minor: 9}},
				"DaemonSet":   recommendedApi{"apps/v1", config.Semver{Major: 1, Minor: 9}},
			},
			"batch/v1beta1": {
				"CronJob": recommendedApi{"batch/v1", config.Semver{Major: 1, Minor: 21}},
			},
			"policy/v1beta1": {
				"PodDisruptionBudget": recommendedApi{"policy/v1", config.Semver{Major: 1, Minor: 21}},
			},
			"networking.k8s.io/v1beta1": {
				"Ingress":      recommendedApi{"networking.k8s.io/v1", config.Semver{Major: 1, Minor: 19}},
				"IngressClass": recommendedApi{"networking.k8s.io/v1", config.Semver{Major: 1, Minor: 19}},
			},
		}

		score.Grade = scorecard.GradeAllOK

		if inVersion, ok := withStable[meta.TypeMeta.APIVersion]; ok {
			if recAPI, ok := inVersion[meta.TypeMeta.Kind]; ok {

				// The recommended replacement is not available in the version of Kubernetes
				// that the user is using
				if kubernetsVersion.LessThan(recAPI.availableSince) {
					return
				}

				score.Grade = scorecard.GradeWarning
				score.AddComment("",
					fmt.Sprintf("The apiVersion and kind %s/%s is deprecated", meta.TypeMeta.APIVersion, meta.TypeMeta.Kind),
					fmt.Sprintf("It's recommended to use %s instead which has been available since Kubernetes %s", recAPI.newAPI, recAPI.availableSince.String()),
				)
				return
			}
		}

		return
	}
}
0707010000008A000081A4000000000000000000000001680FB1F800000BCE000000000000000000000000000000000000003600000000kube-score-1.20.0/score/stable/stable_version_test.gopackage stable

import (
	"testing"

	"github.com/stretchr/testify/assert"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/zegl/kube-score/config"
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
)

func TestStableVersionOldKubernetesVersion(t *testing.T) {
	oldKubernetes := metaStableAvailable(config.Semver{Major: 1, Minor: 4})
	scoreOld, _ := oldKubernetes(ks.BothMeta{TypeMeta: v1.TypeMeta{Kind: "Deployment", APIVersion: "extensions/v1beta1"}})
	assert.Equal(t, scorecard.GradeAllOK, scoreOld.Grade)
	assert.Equal(t, []scorecard.TestScoreComment(nil), scoreOld.Comments)
}

func TestStableVersionNewKubernetesVersion(t *testing.T) {
	newKubernetes := metaStableAvailable(config.Semver{Major: 1, Minor: 18})
	scoreNew, _ := newKubernetes(ks.BothMeta{TypeMeta: v1.TypeMeta{Kind: "Deployment", APIVersion: "extensions/v1beta1"}})
	assert.Equal(t, scorecard.GradeWarning, scoreNew.Grade)
	assert.Equal(t, []scorecard.TestScoreComment{{Path: "", Summary: "The apiVersion and kind extensions/v1beta1/Deployment is deprecated", Description: "It's recommended to use apps/v1 instead which has been available since Kubernetes v1.9", DocumentationURL: ""}}, scoreNew.Comments)
}

func TestStableVersionIngress(t *testing.T) {
	newKubernetes := metaStableAvailable(config.Semver{Major: 1, Minor: 20})
	scoreNew, _ := newKubernetes(ks.BothMeta{TypeMeta: v1.TypeMeta{Kind: "Ingress", APIVersion: "extensions/v1beta1"}})
	assert.Equal(t, scorecard.GradeWarning, scoreNew.Grade)
	assert.Equal(t, []scorecard.TestScoreComment{{Path: "", Summary: "The apiVersion and kind extensions/v1beta1/Ingress is deprecated", Description: "It's recommended to use networking.k8s.io/v1 instead which has been available since Kubernetes v1.19", DocumentationURL: ""}}, scoreNew.Comments)
}

func TestStableVersionPodDisruptionBudget(t *testing.T) {
	newKubernetes := metaStableAvailable(config.Semver{Major: 1, Minor: 21})
	scoreNew, _ := newKubernetes(ks.BothMeta{TypeMeta: v1.TypeMeta{Kind: "PodDisruptionBudget", APIVersion: "policy/v1beta1"}})
	assert.Equal(t, scorecard.GradeWarning, scoreNew.Grade)
	assert.Equal(t, []scorecard.TestScoreComment{{Path: "", Summary: "The apiVersion and kind policy/v1beta1/PodDisruptionBudget is deprecated", Description: "It's recommended to use policy/v1 instead which has been available since Kubernetes v1.21", DocumentationURL: ""}}, scoreNew.Comments)
}

func TestStableNetworkingIngress(t *testing.T) {
	newKubernetes := metaStableAvailable(config.Semver{Major: 1, Minor: 21})
	scoreNew, _ := newKubernetes(ks.BothMeta{TypeMeta: v1.TypeMeta{Kind: "Ingress", APIVersion: "networking.k8s.io/v1beta1"}})
	assert.Equal(t, scorecard.GradeWarning, scoreNew.Grade)
	assert.Equal(t, []scorecard.TestScoreComment{{Path: "", Summary: "The apiVersion and kind networking.k8s.io/v1beta1/Ingress is deprecated", Description: "It's recommended to use networking.k8s.io/v1 instead which has been available since Kubernetes v1.19", DocumentationURL: ""}}, scoreNew.Comments)
}
0707010000008B000081A4000000000000000000000001680FB1F800000ADC000000000000000000000000000000000000002F00000000kube-score-1.20.0/score/stable_version_test.gopackage score

import (
	"testing"

	"github.com/zegl/kube-score/config"
	ks "github.com/zegl/kube-score/domain"
	"github.com/zegl/kube-score/scorecard"
)

func TestStatefulSetAppsv1beta1(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-appsv1beta1.yaml", "Stable version", scorecard.GradeWarning)
}

func TestStatefulSetAppsv1beta1Kubernetes1dot4(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("statefulset-appsv1beta1.yaml")}, nil, &config.RunConfiguration{
		KubernetesVersion: config.Semver{Major: 1, Minor: 4},
	}, "Stable version", scorecard.GradeAllOK)
}

func TestStatefulSetAppsv1beta1Kubernetes1dot18(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("statefulset-appsv1beta1.yaml")}, nil, &config.RunConfiguration{
		KubernetesVersion: config.Semver{Major: 1, Minor: 18},
	}, "Stable version", scorecard.GradeWarning)
}

func TestStatefulSetAppsv1beta2(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "statefulset-appsv1beta2.yaml", "Stable version", scorecard.GradeWarning)
}

func TestDeploymentExtensionsv1beta1(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-extensions-v1beta1.yaml", "Stable version", scorecard.GradeWarning)
}

func TestDeploymentAppsv1beta1(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-appsv1beta1.yaml", "Stable version", scorecard.GradeWarning)
}

func TestDeploymentAppsv1beta2(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "deployment-appsv1beta2.yaml", "Stable version", scorecard.GradeWarning)
}

func TestDaemonSetAppsv1(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "daemonset-appsv1.yaml", "Stable version", scorecard.GradeAllOK)
}

func TestDaemonSetAppsv1beta2(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "daemonset-appsv1beta2.yaml", "Stable version", scorecard.GradeWarning)
}

func TestDaemonSetExtensionsv1beta1(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "daemonset-extensionsv1beta1.yaml", "Stable version", scorecard.GradeWarning)
}

func TestCronJobBatchv1beta1(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "cronjob-batchv1beta1.yaml", "Stable version", scorecard.GradeAllOK)
}

func TestCronJobBatchv1beta1Kubernetes1dot21(t *testing.T) {
	t.Parallel()
	testExpectedScoreWithConfig(t, []ks.NamedReader{testFile("cronjob-batchv1beta1.yaml")}, nil, &config.RunConfiguration{
		KubernetesVersion: config.Semver{Major: 1, Minor: 21},
	}, "Stable version", scorecard.GradeWarning)
}

func TestCronJobBatchv1(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "cronjob-batchv1.yaml", "Stable version", scorecard.GradeAllOK)
}

func TestJobBatchv1(t *testing.T) {
	t.Parallel()
	testExpectedScore(t, "job-batchv1.yaml", "Stable version", scorecard.GradeAllOK)
}
0707010000008C000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002100000000kube-score-1.20.0/score/testdata0707010000008D000081A4000000000000000000000001680FB1F80000034E000000000000000000000000000000000000002D00000000kube-score-1.20.0/score/testdata/all-ok.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: testspace
  labels:
    app: foo-all-ok
spec:
  containers:
    - name: foobar
      image: foo/bar:123
      imagePullPolicy: Always
      resources:
        requests:
          cpu: 1
          memory: 1
        limits:
          cpu: 1
          memory: 1
      readinessProbe:
        httpGet:
          path: /ready
          port: 8080
      livenessProbe:
        httpGet:
          path: /live
          port: 8080
      securityContext:
        privileged: False
        runAsUser: 30000
        runAsGroup: 30000
        readOnlyRootFilesystem: True

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: foo-all-ok-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: foo-all-ok
  policyTypes:
    - Egress
    - Ingress
0707010000008E000081A4000000000000000000000001680FB1F8000000A4000000000000000000000000000000000000003B00000000kube-score-1.20.0/score/testdata/configmap-multi-dash.yaml---
apiVersion: v1
kind: ConfigMap
metadata:
  name: awesome
data:
  awesome.crt: |
    -----BEGIN CERTIFICATE-----
    TEST_DATA
    -----END CERTIFICATE-----
---
0707010000008F000081A4000000000000000000000001680FB1F800000183000000000000000000000000000000000000004700000000kube-score-1.20.0/score/testdata/cronjob-batchv1-deadline-not-set.yamlapiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: hello
              image: busybox
              args:
                - /bin/sh
                - -c
                - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure07070100000090000081A4000000000000000000000001680FB1F8000001A2000000000000000000000000000000000000004300000000kube-score-1.20.0/score/testdata/cronjob-batchv1-deadline-set.yamlapiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  startingDeadlineSeconds: 100
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: hello
              image: busybox
              args:
                - /bin/sh
                - -c
                - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure07070100000091000081A4000000000000000000000001680FB1F8000002AB000000000000000000000000000000000000004C00000000kube-score-1.20.0/score/testdata/cronjob-batchv1-restartpolicy-invalid.yamlkind: CronJob
apiVersion: batch/v1
metadata:
  namespace: cronjobs
  name: pwsh-test
spec:
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: pwsh
            imagePullPolicy: Always
            image: mcr.microsoft.com/powershell:7
            command:
            - pwsh
            - -Command
            - Start-Sleep -Seconds 5
            securityContext:
              readOnlyRootFilesystem: true
            resources:
              limits:
                ephemeral-storage: 50Mi
              requests:
                ephemeral-storage: 50Mi
          RestartPolicy: Once
  schedule: '0/1 * * * *'
  startingDeadlineSeconds: 5
07070100000092000081A4000000000000000000000001680FB1F80000017F000000000000000000000000000000000000004C00000000kube-score-1.20.0/score/testdata/cronjob-batchv1-restartpolicy-not-set.yamlapiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
07070100000093000081A4000000000000000000000001680FB1F8000001A2000000000000000000000000000000000000004A00000000kube-score-1.20.0/score/testdata/cronjob-batchv1-restartpolicy-valid.yamlapiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure
07070100000094000081A4000000000000000000000001680FB1F8000000E0000000000000000000000000000000000000003600000000kube-score-1.20.0/score/testdata/cronjob-batchv1.yamlapiVersion: batch/v1
kind: CronJob
metadata:
  name: cronjob-test
spec:
  schedule: "1 3 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: foo
            image: bar:latest07070100000095000081A4000000000000000000000001680FB1F800000188000000000000000000000000000000000000004C00000000kube-score-1.20.0/score/testdata/cronjob-batchv1beta1-deadline-not-set.yamlapiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: hello
              image: busybox
              args:
                - /bin/sh
                - -c
                - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure07070100000096000081A4000000000000000000000001680FB1F8000001A7000000000000000000000000000000000000004800000000kube-score-1.20.0/score/testdata/cronjob-batchv1beta1-deadline-set.yamlapiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  startingDeadlineSeconds: 100
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: hello
              image: busybox
              args:
                - /bin/sh
                - -c
                - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure07070100000097000081A4000000000000000000000001680FB1F8000002B2000000000000000000000000000000000000005100000000kube-score-1.20.0/score/testdata/cronjob-batchv1beta1-restartpolicy-invalid.yamlkind: CronJob
apiVersion: batch/v1beta1
metadata:
  namespace: cronjobs
  name: pwsh-test
spec:
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: pwsh
            imagePullPolicy: Always
            image: mcr.microsoft.com/powershell:7
            command:
            - pwsh
            - -Command
            - Start-Sleep -Seconds 5
            securityContext:
              readOnlyRootFilesystem: true
            resources:
              limits:
                ephemeral-storage: 50Mi
              requests:
                ephemeral-storage: 50Mi
          RestartPolicy: OnFailure 
schedule: '0/1 * * * *'
startingDeadlineSeconds: 5
07070100000098000081A4000000000000000000000001680FB1F800000184000000000000000000000000000000000000005100000000kube-score-1.20.0/score/testdata/cronjob-batchv1beta1-restartpolicy-not-set.yamlapiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
07070100000099000081A4000000000000000000000001680FB1F8000001A7000000000000000000000000000000000000004F00000000kube-score-1.20.0/score/testdata/cronjob-batchv1beta1-restartpolicy-valid.yamlapiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox:1.28
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure
0707010000009A000081A4000000000000000000000001680FB1F8000000E5000000000000000000000000000000000000003B00000000kube-score-1.20.0/score/testdata/cronjob-batchv1beta1.yamlapiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cronjob-test
spec:
  schedule: "1 3 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: foo
            image: bar:latest0707010000009B000081A4000000000000000000000001680FB1F80000009E000000000000000000000000000000000000003700000000kube-score-1.20.0/score/testdata/daemonset-appsv1.yamlapiVersion: apps/v1
kind: DaemonSet
metadata:
  name: daemonset-test
spec:
  template:
    spec:
      containers:
      - name: foo
        image: foo:latest0707010000009C000081A4000000000000000000000001680FB1F8000000A3000000000000000000000000000000000000003C00000000kube-score-1.20.0/score/testdata/daemonset-appsv1beta2.yamlapiVersion: apps/v1beta2
kind: DaemonSet
metadata:
  name: daemonset-test
spec:
  template:
    spec:
      containers:
      - name: foo
        image: foo:latest0707010000009D000081A4000000000000000000000001680FB1F8000000A9000000000000000000000000000000000000004200000000kube-score-1.20.0/score/testdata/daemonset-extensionsv1beta1.yamlapiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: daemonset-test
spec:
  template:
    spec:
      containers:
      - name: foo
        image: foo:latest0707010000009E000081A4000000000000000000000001680FB1F8000000FF000000000000000000000000000000000000003D00000000kube-score-1.20.0/score/testdata/deployment-appsv1beta1.yamlapiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: deployment-test-1
spec:
  template:
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
        resources:
          limits:
            cpu: 200m
            memory: 1Gi
0707010000009F000081A4000000000000000000000001680FB1F8000000FF000000000000000000000000000000000000003D00000000kube-score-1.20.0/score/testdata/deployment-appsv1beta2.yamlapiVersion: apps/v1beta2
kind: Deployment
metadata:
  name: deployment-test-1
spec:
  template:
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
        resources:
          limits:
            cpu: 200m
            memory: 1Gi
070701000000A0000081A4000000000000000000000001680FB1F800000105000000000000000000000000000000000000004400000000kube-score-1.20.0/score/testdata/deployment-extensions-v1beta1.yamlapiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: deployment-test-1
spec:
  template:
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
        resources:
          limits:
            cpu: 200m
            memory: 1Gi
070701000000A1000081A4000000000000000000000001680FB1F8000000F3000000000000000000000000000000000000004D00000000kube-score-1.20.0/score/testdata/deployment-host-antiaffinity-1-replica.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-host-antiaffinity-1-replica
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar
070701000000A2000081A4000000000000000000000001680FB1F8000000EE000000000000000000000000000000000000004B00000000kube-score-1.20.0/score/testdata/deployment-host-antiaffinity-not-set.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-anti-affinity-not-set
spec:
  replicas: 10
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar
070701000000A3000081A4000000000000000000000001680FB1F800000286000000000000000000000000000000000000006100000000kube-score-1.20.0/score/testdata/deployment-host-antiaffinity-preffered-selector-expression.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-preffered-anti-affinity
spec:
  replicas: 10
  template:
    metadata:
      labels:
        app: foo
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - foo
                  - another-foo
              topologyKey: kubernetes.io/hostname
      containers:
      - name: foobar
        image: foo:bar
070701000000A4000081A4000000000000000000000001680FB1F800000214000000000000000000000000000000000000005F00000000kube-score-1.20.0/score/testdata/deployment-host-antiaffinity-preffered-selector-no-match.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-preffered-anti-affinity
spec:
  replicas: 10
  template:
    metadata:
      labels:
        app: foo
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchLabels:
                  app: not-foo
              topologyKey: kubernetes.io/hostname
      containers:
      - name: foobar
        image: foo:bar
070701000000A5000081A4000000000000000000000001680FB1F800000210000000000000000000000000000000000000004D00000000kube-score-1.20.0/score/testdata/deployment-host-antiaffinity-preffered.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-preffered-anti-affinity
spec:
  replicas: 10
  template:
    metadata:
      labels:
        app: foo
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchLabels:
                  app: foo
              topologyKey: kubernetes.io/hostname
      containers:
      - name: foobar
        image: foo:bar
070701000000A6000081A4000000000000000000000001680FB1F8000001E9000000000000000000000000000000000000004C00000000kube-score-1.20.0/score/testdata/deployment-host-antiaffinity-required.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-required-anti-affinity
spec:
  replicas: 10
  template:
    metadata:
      labels:
        app: foo
    spec:
      replicas: 10
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
           - topologyKey: "kubernetes.io/hostname"
             labelSelector:
               matchLabels:
                app: foo
      containers:
      - name: foobar
        image: foo:bar
070701000000A7000081A4000000000000000000000001680FB1F8000002E8000000000000000000000000000000000000005C00000000kube-score-1.20.0/score/testdata/deployment-poddisruptionbudget-v1-different-namespace.yamlapiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-budget
  namespace: not-foo
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: foo
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-budget
  namespace: bar
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: foo
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-budget-not-matching
  namespace: baz
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: not-foo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statefulset-test-1
  namespace: foo
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar070701000000A8000081A4000000000000000000000001680FB1F800000165000000000000000000000000000000000000005000000000kube-score-1.20.0/score/testdata/deployment-poddisruptionbudget-v1-matches.yamlapiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: foo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statefulset-test-1
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar070701000000A9000081A4000000000000000000000001680FB1F800000169000000000000000000000000000000000000005100000000kube-score-1.20.0/score/testdata/deployment-poddisruptionbudget-v1-no-match.yamlapiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: not-foo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statefulset-test-1
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar070701000000AA000081A4000000000000000000000001680FB1F800000153000000000000000000000000000000000000005200000000kube-score-1.20.0/score/testdata/deployment-poddisruptionbudget-v1-no-policy.yamlapiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  selector:
    matchLabels:
      app: foo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statefulset-test-1
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar070701000000AB000081A4000000000000000000000001680FB1F8000001A2000000000000000000000000000000000000006000000000kube-score-1.20.0/score/testdata/deployment-poddisruptionbudget-v1beta1-expression-matches.yamlapiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  minAvailable: 2
  selector:
    matchExpressions:
    - key: app
      operator: In
      values: [foo, foo1, foo2]
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statefulset-test-1
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar070701000000AC000081A4000000000000000000000001680FB1F8000001A8000000000000000000000000000000000000006100000000kube-score-1.20.0/score/testdata/deployment-poddisruptionbudget-v1beta1-expression-no-match.yamlapiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  minAvailable: 2
  selector:
    matchExpressions:
    - key: app
      operator: In
      values: ["not-foo", "not-foo2"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statefulset-test-1
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar070701000000AD000081A4000000000000000000000001680FB1F80000016A000000000000000000000000000000000000005500000000kube-score-1.20.0/score/testdata/deployment-poddisruptionbudget-v1beta1-matches.yamlapiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: foo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statefulset-test-1
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar070701000000AE000081A4000000000000000000000001680FB1F80000016E000000000000000000000000000000000000005600000000kube-score-1.20.0/score/testdata/deployment-poddisruptionbudget-v1beta1-no-match.yamlapiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: not-foo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statefulset-test-1
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar070701000000AF000081A4000000000000000000000001680FB1F800000158000000000000000000000000000000000000005700000000kube-score-1.20.0/score/testdata/deployment-poddisruptionbudget-v1beta1-no-policy.yamlapiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  selector:
    matchLabels:
      app: foo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statefulset-test-1
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar070701000000B0000081A4000000000000000000000001680FB1F8000000FC000000000000000000000000000000000000004000000000kube-score-1.20.0/score/testdata/deployment-test-resources.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-test-1
spec:
  template:
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
        resources:
          limits:
            cpu: 200m
            memory: 1Gi
  070701000000B1000081A4000000000000000000000001680FB1F80000023F000000000000000000000000000000000000004700000000kube-score-1.20.0/score/testdata/deployment-with-hpa-has-replicas.yamlapiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
  namespace: default
spec:
  replicas: 123
  template:
    spec:
      containers:
        - name: foo
          image: foo:latest070701000000B2000081A4000000000000000000000001680FB1F80000022F000000000000000000000000000000000000004B00000000kube-score-1.20.0/score/testdata/deployment-with-hpa-not-has-replicas.yamlapiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
  namespace: default
spec:
  template:
    spec:
      containers:
        - name: foo
          image: foo:latest070701000000B3000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002600000000kube-score-1.20.0/score/testdata/helm070701000000B4000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002B00000000kube-score-1.20.0/score/testdata/helm/app1070701000000B5000081A4000000000000000000000001680FB1F80000001B000000000000000000000000000000000000003600000000kube-score-1.20.0/score/testdata/helm/app1/Chart.yamlname: app1
version: v1.0.0
070701000000B6000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000003500000000kube-score-1.20.0/score/testdata/helm/app1/templates070701000000B7000081A4000000000000000000000001680FB1F8000000FA000000000000000000000000000000000000004500000000kube-score-1.20.0/score/testdata/helm/app1/templates/deployment.yamlkind: Deployment
apiVersion: apps/v1
metadata:
  name: foo
spec:
  template:
    metadata:
      labels:
        foo: bar
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: foo2
spec:
  template:
    metadata:
      labels:
        foo: bar2
070701000000B8000081A4000000000000000000000001680FB1F80000022F000000000000000000000000000000000000004B00000000kube-score-1.20.0/score/testdata/hpa-autoscalingv1-targets-deployment.yamlapiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
  namespace: default
spec:
  template:
    spec:
      containers:
        - name: foo
          image: foo:latest070701000000B9000081A4000000000000000000000001680FB1F80000022F000000000000000000000000000000000000004B00000000kube-score-1.20.0/score/testdata/hpa-autoscalingv2-targets-deployment.yamlapiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
  namespace: default
spec:
  template:
    spec:
      containers:
        - name: foo
          image: foo:latest070701000000BA000081A4000000000000000000000001680FB1F800000233000000000000000000000000000000000000003800000000kube-score-1.20.0/score/testdata/hpa-has-no-target.yamlapiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-not-apache
  namespace: default
spec:
  template:
    spec:
      containers:
        - name: foo
          image: foo:latest070701000000BB000081A4000000000000000000000001680FB1F80000022F000000000000000000000000000000000000003B00000000kube-score-1.20.0/score/testdata/hpa-min-replicas-nok.yamlapiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
  namespace: default
spec:
  template:
    spec:
      containers:
        - name: foo
          image: foo:latest070701000000BC000081A4000000000000000000000001680FB1F80000022F000000000000000000000000000000000000003A00000000kube-score-1.20.0/score/testdata/hpa-min-replicas-ok.yamlapiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
  namespace: default
spec:
  template:
    spec:
      containers:
        - name: foo
          image: foo:latest070701000000BD000081A4000000000000000000000001680FB1F800000325000000000000000000000000000000000000003C00000000kube-score-1.20.0/score/testdata/hpa-target-deployment.yamlapiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: php-apache
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: php-apache
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 50
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-apache
  namespace: default
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: foo
          image: foo:latest
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: default
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080070701000000BE000081A4000000000000000000000001680FB1F800000106000000000000000000000000000000000000004000000000kube-score-1.20.0/score/testdata/ignore-annotation-service.yamlapiVersion: v1
kind: Service
metadata:
  name: node-port-service-with-ignore
  namespace: foospace
  annotations:
    kube-score/ignore: service-type
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: NodePort
070701000000BF000081A4000000000000000000000001680FB1F80000024D000000000000000000000000000000000000005400000000kube-score-1.20.0/score/testdata/ingress-networkingv1-targets-service-no-match.yamlapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: testspace
spec:
  rules:
  - http:
      paths:
      - path: /app
        backend:
          service:
            name: app-service
            port:
              number: 5601

      - path: /no-match
        backend:
          service:
            name: app-service-123123
            port:
              number: 5601

---
kind: Service
apiVersion: v1
metadata:
  name: app-service
  namespace: testspace
spec:
  selector:
    app: kibana
  ports:
  - name: http
    protocol: TCP
    port: 5601
070701000000C0000081A4000000000000000000000001680FB1F8000001BD000000000000000000000000000000000000004B00000000kube-score-1.20.0/score/testdata/ingress-networkingv1-targets-service.yamlapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: testspace
spec:
  rules:
  - http:
      paths:
      - path: /app
        backend:
          service:
            name: app-service
            port:
              number: 5601
---
kind: Service
apiVersion: v1
metadata:
  name: app-service
  namespace: testspace
spec:
  selector:
    app: kibana
  ports:
  - name: http
    protocol: TCP
    port: 5601
070701000000C1000081A4000000000000000000000001680FB1F800000214000000000000000000000000000000000000005900000000kube-score-1.20.0/score/testdata/ingress-networkingv1beta1-targets-service-no-match.yamlapiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: app-ingress
  namespace: testspace
spec:
  rules:
  - http:
      paths:
      - path: /app
        backend:
          serviceName: app-service
          servicePort: 5601

      - path: /no-match
        backend:
          serviceName: app-service-123123
          servicePort: 5601

---
kind: Service
apiVersion: v1
metadata:
  name: app-service
  namespace: testspace
spec:
  selector:
    app: kibana
  ports:
  - name: http
    protocol: TCP
    port: 5601
070701000000C2000081A4000000000000000000000001680FB1F8000001A3000000000000000000000000000000000000005000000000kube-score-1.20.0/score/testdata/ingress-networkingv1beta1-targets-service.yamlapiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: app-ingress
  namespace: testspace
spec:
  rules:
  - http:
      paths:
      - path: /app
        backend:
          serviceName: app-service
          servicePort: 5601
---
kind: Service
apiVersion: v1
metadata:
  name: app-service
  namespace: testspace
spec:
  selector:
    app: kibana
  ports:
  - name: http
    protocol: TCP
    port: 5601
070701000000C3000081A4000000000000000000000001680FB1F80000020D000000000000000000000000000000000000004700000000kube-score-1.20.0/score/testdata/ingress-targets-service-no-match.yamlapiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: app-ingress
  namespace: testspace
spec:
  rules:
  - http:
      paths:
      - path: /app
        backend:
          serviceName: app-service
          servicePort: 5601

      - path: /no-match
        backend:
          serviceName: app-service-123123
          servicePort: 5601

---
kind: Service
apiVersion: v1
metadata:
  name: app-service
  namespace: testspace
spec:
  selector:
    app: kibana
  ports:
  - name: http
    protocol: TCP
    port: 5601
070701000000C4000081A4000000000000000000000001680FB1F80000019C000000000000000000000000000000000000003E00000000kube-score-1.20.0/score/testdata/ingress-targets-service.yamlapiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: app-ingress
  namespace: testspace
spec:
  rules:
  - http:
      paths:
      - path: /app
        backend:
          serviceName: app-service
          servicePort: 5601
---
kind: Service
apiVersion: v1
metadata:
  name: app-service
  namespace: testspace
spec:
  selector:
    app: kibana
  ports:
  - name: http
    protocol: TCP
    port: 5601
070701000000C5000081A4000000000000000000000001680FB1F8000000F7000000000000000000000000000000000000004100000000kube-score-1.20.0/score/testdata/ingress-v1-invalid-backend.yamlapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: whatever
spec:
  rules:
    - host: abc.example.test
      http:
        paths:
          - path: /
            backend:
              serviceName: abc
              servicePort: def070701000000C6000081A4000000000000000000000001680FB1F800000276000000000000000000000000000000000000003700000000kube-score-1.20.0/score/testdata/ingress_issue363.yamlapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: testspace
spec:
  rules:
    - host: foo.bar.network
      http:
        # Note: This input does _NOT_ match the spec.
        # This test is here to make sure that there are no panics on invalid inputs.
        paths:
          - backend:
              serviceName: app-service
              servicePort: http
  tls:
    - hosts:
        - foo.bar.network
---
kind: Service
apiVersion: v1
metadata:
  name: app-service
  namespace: testspace
spec:
  selector:
    app: kibana
  ports:
    - name: http
      protocol: TCP
      port: 5601
070701000000C7000081A4000000000000000000000001680FB1F800000111000000000000000000000000000000000000003700000000kube-score-1.20.0/score/testdata/ingress_issue388.yamlapiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  namespace: testspace
spec:
  rules:
    - host: foo.bar.network
      # No http block — such ingresses can be used for redirects in nginx-ingress
  tls:
    - hosts:
        - foo.bar.network
070701000000C8000081A4000000000000000000000001680FB1F8000000FB000000000000000000000000000000000000003200000000kube-score-1.20.0/score/testdata/job-batchv1.yamlapiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    spec:
      containers:
      - name: pi
        image: perl
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Never
  backoffLimit: 4

070701000000C9000081A4000000000000000000000001680FB1F8000001CE000000000000000000000000000000000000004400000000kube-score-1.20.0/score/testdata/kube-score-ignore-annotations.yamlapiVersion: v1
kind: Pod
metadata:
  name: kube-score-ignore-annotations
  annotations:
    "kube-score/ignore": container-security-context-readonlyrootfilesystem,pod-networkpolicy,container-security-context-user-group-id,pod-probes,container-resources
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      limits:
        cpu: 200m
        memory: 1Gi
        ephemeral-storage: 2Gi
      requests:
        cpu: 200m
070701000000CA000081A4000000000000000000000001680FB1F80000014F000000000000000000000000000000000000003700000000kube-score-1.20.0/score/testdata/linenumbers-helm.yaml---
# Source: app1/templates/deployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
  name: foo
spec:
  template:
    metadata:
      labels:
        foo: bar
---
# Source: app1/templates/deployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
  name: foo2
spec:
  template:
    metadata:
      labels:
        foo: bar2070701000000CB000081A4000000000000000000000001680FB1F8000000FD000000000000000000000000000000000000003200000000kube-score-1.20.0/score/testdata/linenumbers.yaml---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: foo
spec:
  template:
    metadata:
      labels:
        foo: bar
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: foo2
spec:
  template:
    metadata:
      labels:
        foo: bar2070701000000CC000081A4000000000000000000000001680FB1F8000002AA000000000000000000000000000000000000002B00000000kube-score-1.20.0/score/testdata/list.yamlapiVersion: v1
kind: List
items:
  - apiVersion: v1
    kind: Service
    metadata:
      name: list-service-test
    spec:
      ports:
        - protocol: TCP
          port: 80
      selector:
        app: list-deployment-test
  - apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: list-deployment-test
      labels:
        app: list-deployment-test
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: list-deployment-test
      template:
        metadata:
          labels:
            app: list-deployment-test
        spec:
          containers:
            - name: nginx
              image: nginx
070701000000CD000081A4000000000000000000000001680FB1F8000001F0000000000000000000000000000000000000004500000000kube-score-1.20.0/score/testdata/networkpolicy-cronjob-matching.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: foo
  policyTypes:
    - Egress
    - Ingress
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cronjob-test-1
  namespace: testspace
spec:
  jobTemplate:
    spec:
      template:
        metadata:
          labels:
            app: foo
        spec:
          containers:
          - name: foobar
            image: foo:bar
070701000000CE000081A4000000000000000000000001680FB1F8000001F4000000000000000000000000000000000000005200000000kube-score-1.20.0/score/testdata/networkpolicy-cronjob-not-matching-selector.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: foo
  policyTypes:
    - Egress
    - Ingress
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: cronjob-test-1
  namespace: testspace
spec:
  jobTemplate:
    spec:
      template:
        metadata:
          labels:
            app: not-foo
        spec:
          containers:
          - name: foobar
            image: foo:bar
070701000000CF000081A4000000000000000000000001680FB1F8000001BB000000000000000000000000000000000000004800000000kube-score-1.20.0/score/testdata/networkpolicy-deployment-matching.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: foo
  policyTypes:
    - Egress
    - Ingress
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-test-1
  namespace: testspace
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
        - name: foobar
          image: foo:bar
070701000000D0000081A4000000000000000000000001680FB1F8000001BF000000000000000000000000000000000000005500000000kube-score-1.20.0/score/testdata/networkpolicy-deployment-not-matching-selector.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: foo
  policyTypes:
    - Egress
    - Ingress
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-test-1
  namespace: testspace
spec:
  template:
    metadata:
      labels:
        app: not-foo
    spec:
      containers:
        - name: foobar
          image: foo:bar
070701000000D1000081A4000000000000000000000001680FB1F8000001C6000000000000000000000000000000000000004900000000kube-score-1.20.0/score/testdata/networkpolicy-matching-only-egress.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: testapp
  egress:
  - ports:
    - port: 53
      protocol: TCP
    - port: 53
      protocol: UDP
  policyTypes:
  - Egress
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: testspace
  labels:
    app: testapp
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
070701000000D2000081A4000000000000000000000001680FB1F8000001C8000000000000000000000000000000000000004A00000000kube-score-1.20.0/score/testdata/networkpolicy-matching-only-ingress.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: testapp
  ingress:
  - ports:
    - port: 53
      protocol: TCP
    - port: 53
      protocol: UDP
  policyTypes:
  - Ingress
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: testspace
  labels:
    app: testapp
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
070701000000D3000081A4000000000000000000000001680FB1F800000249000000000000000000000000000000000000003D00000000kube-score-1.20.0/score/testdata/networkpolicy-matching.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: testapp
  egress:
  - ports:
    - port: 53
      protocol: TCP
    - port: 53
      protocol: UDP
    to:
    - namespaceSelector:
        matchLabels:
          service: kube-system
  policyTypes:
  - Ingress
  - Egress
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: testspace
  labels:
    app: testapp
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
070701000000D4000081A4000000000000000000000001680FB1F80000012B000000000000000000000000000000000000004100000000kube-score-1.20.0/score/testdata/networkpolicy-not-matching.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    app: testapp
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: not-testapp
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
070701000000D5000081A4000000000000000000000001680FB1F8000001E9000000000000000000000000000000000000004900000000kube-score-1.20.0/score/testdata/networkpolicy-statefulset-matching.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: foo
  policyTypes:
    - Egress
    - Ingress
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
  namespace: testspace
spec:
  selector:
    matchLabels:
      app: foo
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
        - name: foobar
          image: foo:bar
070701000000D6000081A4000000000000000000000001680FB1F8000001ED000000000000000000000000000000000000005600000000kube-score-1.20.0/score/testdata/networkpolicy-statefulset-not-matching-selector.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: foo
  policyTypes:
    - Egress
    - Ingress
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
  namespace: testspace
spec:
  selector:
    matchLabels:
      app: foo
  template:
    metadata:
      labels:
        app: not-foo
    spec:
      containers:
        - name: foobar
          image: foo:bar
070701000000D7000081A4000000000000000000000001680FB1F800000181000000000000000000000000000000000000004500000000kube-score-1.20.0/score/testdata/networkpolicy-targets-all-pods.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector: {}
  ingress:
    - {}
  egress:
    - {}
  policyTypes:
    - Ingress
    - Egress
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: testspace
  labels:
    app: testapp
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
070701000000D8000081A4000000000000000000000001680FB1F800000202000000000000000000000000000000000000004B00000000kube-score-1.20.0/score/testdata/networkpolicy-targets-pod-deployment.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: foo
  egress:
  - ports:
    - port: 53
      protocol: TCP
    - port: 53
      protocol: UDP
  policyTypes:
  - Egress
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: statefulset-test-1
  namespace: testspace
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar070701000000D9000081A4000000000000000000000001680FB1F8000001CA000000000000000000000000000000000000004D00000000kube-score-1.20.0/score/testdata/networkpolicy-targets-pod-not-matching.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: not-testapp
  egress:
  - ports:
    - port: 53
      protocol: TCP
    - port: 53
      protocol: UDP
  policyTypes:
  - Egress
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: testspace
  labels:
    app: testapp
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
070701000000DA000081A4000000000000000000000001680FB1F8000001C6000000000000000000000000000000000000004000000000kube-score-1.20.0/score/testdata/networkpolicy-targets-pod.yamlapiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: testapp-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: testapp
  egress:
  - ports:
    - port: 53
      protocol: TCP
    - port: 53
      protocol: UDP
  policyTypes:
  - Egress
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: testspace
  labels:
    app: testapp
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
070701000000DB000081A4000000000000000000000001680FB1F8000003E5000000000000000000000000000000000000005800000000kube-score-1.20.0/score/testdata/pod-container-memory-requests-annotation-optional.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: testspace
  labels:
    app: foo-all-ok
  annotations:
    kube-score/enable: container-resource-requests-equal-limits
spec:
  containers:
    - name: foobar
      image: foo/bar:123
      imagePullPolicy: Always
      resources:
        requests:
          cpu: 1
          memory: 1Gi
          ephemeral-storage: 500Mi
        limits:
          cpu: 1
          memory: 2Gi
          ephemeral-storage: 500Mi
      readinessProbe:
        httpGet:
          path: /ready
          port: 8080
      livenessProbe:
        httpGet:
          path: /live
          port: 8080
      securityContext:
        privileged: False
        runAsUser: 30000
        runAsGroup: 30000
        readOnlyRootFilesystem: True
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: foo-all-ok-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: foo-all-ok
  policyTypes:
    - Egress
    - Ingress070701000000DC000081A4000000000000000000000001680FB1F800000396000000000000000000000000000000000000004400000000kube-score-1.20.0/score/testdata/pod-container-memory-requests.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: testspace
  labels:
    app: foo-all-ok
spec:
  containers:
    - name: foobar
      image: foo/bar:123
      imagePullPolicy: Always
      resources:
        requests:
          cpu: 1
          memory: 1Gi
          ephemeral-storage: 500Mi
        limits:
          cpu: 1
          memory: 2Gi
          ephemeral-storage: 500Mi
      readinessProbe:
        httpGet:
          path: /ready
          port: 8080
      livenessProbe:
        httpGet:
          path: /live
          port: 8080
      securityContext:
        privileged: False
        runAsUser: 30000
        runAsGroup: 30000
        readOnlyRootFilesystem: True
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: foo-all-ok-netpol
  namespace: testspace
spec:
  podSelector:
    matchLabels:
      app: foo-all-ok
  policyTypes:
    - Egress
    - Ingress070701000000DD000081A4000000000000000000000001680FB1F8000001FF000000000000000000000000000000000000004A00000000kube-score-1.20.0/score/testdata/pod-container-ports-duplicate-names.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  labels:
    app: app
spec:
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      nodeSelector:
        beta.kubernetes.io/os: linux
      containers:
      - name: app
        image: app:dev
        imagePullPolicy: "Always"
        ports:
        - name: app
          containerPort: 80
          protocol: TCP
        - name: app 
          containerPort: 8080
          protocol: TCP
070701000000DE000081A4000000000000000000000001680FB1F800000237000000000000000000000000000000000000006400000000kube-score-1.20.0/score/testdata/pod-container-ports-missing-containerport-annotation-optional.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  labels:
    app: app
  annotations:
    kube-score/enable: container-ports-check
spec:
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      nodeSelector:
        beta.kubernetes.io/os: linux
      containers:
      - name: app
        image: app:dev
        imagePullPolicy: "Always"
        ports:
        - name: app
          containerPort: 80
          protocol: TCP
        - name: smtp
          containerPort: 
          protocol: TCP
070701000000DF000081A4000000000000000000000001680FB1F8000001FB000000000000000000000000000000000000005000000000kube-score-1.20.0/score/testdata/pod-container-ports-missing-containerport.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  labels:
    app: app
spec:
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      nodeSelector:
        beta.kubernetes.io/os: linux
      containers:
      - name: app
        image: app:dev
        imagePullPolicy: "Always"
        ports:
        - name: app
          containerPort: 80
          protocol: TCP
        - name: smtp
          containerPort: 
          protocol: TCP
070701000000E0000081A4000000000000000000000001680FB1F80000022D000000000000000000000000000000000000004800000000kube-score-1.20.0/score/testdata/pod-container-ports-name-too-long.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  labels:
    app: app
spec:
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      nodeSelector:
        beta.kubernetes.io/os: linux
      containers:
      - name: app
        image: app:dev
        imagePullPolicy: "Always"
        ports:
        - name: my-very-long-port-name-exceeds-fifteen-characters
          containerPort: 80
          protocol: TCP
        - name: app 
          containerPort: 8080
          protocol: TCP
070701000000E1000081A4000000000000000000000001680FB1F80000022E000000000000000000000000000000000000003D00000000kube-score-1.20.0/score/testdata/pod-container-ports-ok.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  labels:
    app: app
spec:
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      nodeSelector:
        beta.kubernetes.io/os: linux
      containers:
      - name: app
        image: app:dev
        imagePullPolicy: "Always"
        ports:
        - name: app
          containerPort: 80
          hostPort: 8080
          protocol: TCP
        - name: smtp
          containerPort: 25
          hostPort: 465
          protocol: TCP
070701000000E2000081A4000000000000000000000001680FB1F800000123000000000000000000000000000000000000003900000000kube-score-1.20.0/score/testdata/pod-env-duplicated.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    env:
    - name: foo
      value: foo
    - name: bar
      value: bar
    - name: bar
      value: bar
    - name: baz
      value: baz
    - name: baz
      value: baz
070701000000E3000081A4000000000000000000000001680FB1F8000000C0000000000000000000000000000000000000003100000000kube-score-1.20.0/score/testdata/pod-env-ok.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    env:
    - name: foo
      value: foo
    - name: bar
      value: bar
070701000000E4000081A4000000000000000000000001680FB1F800000151000000000000000000000000000000000000004E00000000kube-score-1.20.0/score/testdata/pod-ephemeral-storage-annotation-ignore.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-ephemeral-storage-annotation-ignore
  annotations:
    "kube-score/ignore": 'container-resources'
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      limits:
        cpu: 200m
        memory: 1Gi
        ephemeral-storage: 2Gi
      requests:
        cpu: 200m
070701000000E5000081A4000000000000000000000001680FB1F80000010A000000000000000000000000000000000000004A00000000kube-score-1.20.0/score/testdata/pod-ephemeral-storage-missing-limit.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      limits:
        cpu: 200m
        memory: 1Gi
      requests:
        cpu: 200m
        memory: 1Gi
        ephemeral-storage: 2Gi
070701000000E6000081A4000000000000000000000001680FB1F80000010A000000000000000000000000000000000000004C00000000kube-score-1.20.0/score/testdata/pod-ephemeral-storage-missing-request.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      limits:
        cpu: 200m
        memory: 1Gi
        ephemeral-storage: 2Gi
      requests:
        cpu: 200m
        memory: 1Gi
070701000000E7000081A4000000000000000000000001680FB1F800000129000000000000000000000000000000000000005200000000kube-score-1.20.0/score/testdata/pod-ephemeral-storage-request-matches-limit.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      limits:
        cpu: 200m
        memory: 1Gi
        ephemeral-storage: 2Gi
      requests:
        cpu: 200m
        memory: 1Gi
        ephemeral-storage: 2Gi
070701000000E8000081A4000000000000000000000001680FB1F800000180000000000000000000000000000000000000006600000000kube-score-1.20.0/score/testdata/pod-ephemeral-storage-request-nomatch-limit-annotation-optional.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  annotations:
    kube-score/enable: container-ephemeral-storage-request-equals-limit
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      limits:
        cpu: 200m
        memory: 1Gi
        ephemeral-storage: 2Gi
      requests:
        cpu: 200m
        memory: 1Gi
        ephemeral-storage: 1Gi
070701000000E9000081A4000000000000000000000001680FB1F800000129000000000000000000000000000000000000005200000000kube-score-1.20.0/score/testdata/pod-ephemeral-storage-request-nomatch-limit.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      limits:
        cpu: 200m
        memory: 1Gi
        ephemeral-storage: 2Gi
      requests:
        cpu: 200m
        memory: 1Gi
        ephemeral-storage: 1Gi
070701000000EA000081A4000000000000000000000001680FB1F80000008E000000000000000000000000000000000000004200000000kube-score-1.20.0/score/testdata/pod-image-pullpolicy-always.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    imagePullPolicy: Always
070701000000EB000081A4000000000000000000000001680FB1F80000008D000000000000000000000000000000000000004100000000kube-score-1.20.0/score/testdata/pod-image-pullpolicy-never.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    imagePullPolicy: Never
070701000000EC000081A4000000000000000000000001680FB1F800000075000000000000000000000000000000000000005000000000kube-score-1.20.0/score/testdata/pod-image-pullpolicy-undefined-latest-tag.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
070701000000ED000081A4000000000000000000000001680FB1F80000006E000000000000000000000000000000000000004C00000000kube-score-1.20.0/score/testdata/pod-image-pullpolicy-undefined-no-tag.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar
070701000000EE000081A4000000000000000000000001680FB1F800000072000000000000000000000000000000000000004500000000kube-score-1.20.0/score/testdata/pod-image-pullpolicy-undefined.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
070701000000EF000081A4000000000000000000000001680FB1F800000072000000000000000000000000000000000000003A00000000kube-score-1.20.0/score/testdata/pod-image-tag-fixed.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
070701000000F0000081A4000000000000000000000001680FB1F800000075000000000000000000000000000000000000003B00000000kube-score-1.20.0/score/testdata/pod-image-tag-latest.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
070701000000F1000081A4000000000000000000000001680FB1F80000012D000000000000000000000000000000000000003D00000000kube-score-1.20.0/score/testdata/pod-probes-all-missing.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: test
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: test
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080070701000000F2000081A4000000000000000000000001680FB1F8000001C1000000000000000000000000000000000000003600000000kube-score-1.20.0/score/testdata/pod-probes-both.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: test
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
    livenessProbe:
      httpGet:
        path: /live
        port: 8080
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: test
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080070701000000F3000081A4000000000000000000000001680FB1F8000001CE000000000000000000000000000000000000004000000000kube-score-1.20.0/score/testdata/pod-probes-identical-exec.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: test
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    readinessProbe:
      exec:
        command:
        - "abc"
        - "123"
    livenessProbe:
      exec:
        command:
        - "abc"
        - "123"
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: test
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080070701000000F4000081A4000000000000000000000001680FB1F8000001C4000000000000000000000000000000000000004000000000kube-score-1.20.0/score/testdata/pod-probes-identical-http.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: test
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
    livenessProbe:
      httpGet:
        path: /ready
        port: 8080

---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: test
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
070701000000F5000081A4000000000000000000000001680FB1F80000019C000000000000000000000000000000000000003F00000000kube-score-1.20.0/score/testdata/pod-probes-identical-tcp.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: test
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    readinessProbe:
      tcpSocket:
        port: 8080
    livenessProbe:
      tcpSocket:
        port: 8080
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: test
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080070701000000F6000081A4000000000000000000000001680FB1F800000179000000000000000000000000000000000000003E00000000kube-score-1.20.0/score/testdata/pod-probes-missing-live.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: test
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080

---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: test
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080070701000000F7000081A4000000000000000000000001680FB1F800000177000000000000000000000000000000000000003F00000000kube-score-1.20.0/score/testdata/pod-probes-missing-ready.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: test
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    livenessProbe:
      httpGet:
        path: /live
        port: 8080

---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: test
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080070701000000F8000081A4000000000000000000000001680FB1F8000001AB000000000000000000000000000000000000005600000000kube-score-1.20.0/score/testdata/pod-probes-not-targeted-by-service-multi-labels.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: my-app
    otherLabel: foo
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    livenessProbe:
      httpGet:
        path: /ready
        port: 8080
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app-not-matching
    otherLabel: foo
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
070701000000F9000081A4000000000000000000000001680FB1F800000183000000000000000000000000000000000000004900000000kube-score-1.20.0/score/testdata/pod-probes-not-targeted-by-service.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: my-app
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    livenessProbe:
      httpGet:
        path: /ready
        port: 8080
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app-not-matching
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
070701000000FA000081A4000000000000000000000001680FB1F800000221000000000000000000000000000000000000004E00000000kube-score-1.20.0/score/testdata/pod-probes-on-different-containers-init.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: test
spec:
  initContainers:
  - name: foobar2
    image: foo/bar:latest
  containers:
  - name: foobar
    image: foo/bar:latest
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
    livenessProbe:
      httpGet:
        path: /live
        port: 8080

---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: test
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080070701000000FB000081A4000000000000000000000001680FB1F80000020E000000000000000000000000000000000000004900000000kube-score-1.20.0/score/testdata/pod-probes-on-different-containers.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: test
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
    livenessProbe:
      httpGet:
        path: /live
        port: 8080
  - name: foobar2
    image: foo/bar:latest

---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: test
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080070701000000FC000081A4000000000000000000000001680FB1F8000001A6000000000000000000000000000000000000005900000000kube-score-1.20.0/score/testdata/pod-probes-targeted-by-service-different-namespace.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: foospace
  labels:
    app: my-app
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    livenessProbe:
      httpGet:
        path: /ready
        port: 8080
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: not-foospace
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
070701000000FD000081A4000000000000000000000001680FB1F8000001BC000000000000000000000000000000000000006100000000kube-score-1.20.0/score/testdata/pod-probes-targeted-by-service-same-namespace-multi-labels.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: foospace
  labels:
    app: my-app
    foo: bar
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    livenessProbe:
      httpGet:
        path: /ready
        port: 8080
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: foospace
spec:
  selector:
    app: my-app
    foo: bar
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
070701000000FE000081A4000000000000000000000001680FB1F8000001A2000000000000000000000000000000000000005400000000kube-score-1.20.0/score/testdata/pod-probes-targeted-by-service-same-namespace.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: foospace
  labels:
    app: my-app
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    livenessProbe:
      httpGet:
        path: /ready
        port: 8080
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: foospace
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
070701000000FF000081A4000000000000000000000001680FB1F800000176000000000000000000000000000000000000004500000000kube-score-1.20.0/score/testdata/pod-probes-targeted-by-service.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: my-app
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    livenessProbe:
      httpGet:
        path: /ready
        port: 8080
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
07070100000100000081A4000000000000000000000001680FB1F800000109000000000000000000000000000000000000005000000000kube-score-1.20.0/score/testdata/pod-seccomp-annotated-annotation-optional.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  annotations:
    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'runtime/default'
    kube-score/enable: container-seccomp-profile
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    07070100000101000081A4000000000000000000000001680FB1F8000000D8000000000000000000000000000000000000003C00000000kube-score-1.20.0/score/testdata/pod-seccomp-annotated.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  annotations:
    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'runtime/default'
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    07070100000102000081A4000000000000000000000001680FB1F800000129000000000000000000000000000000000000004D00000000kube-score-1.20.0/score/testdata/pod-seccomp-container-securecontext-ok.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    securityContext:
      privileged: False
      runAsUser: 30000
      runAsGroup: 30000
      readOnlyRootFilesystem: True
      seccompProfile:
        type: RuntimeDefault 

07070100000103000081A4000000000000000000000001680FB1F8000002AF000000000000000000000000000000000000005200000000kube-score-1.20.0/score/testdata/pod-seccomp-container-securecontext-warning.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    securityContext:
      privileged: False
      runAsUser: 30000
      runAsGroup: 30000
      readOnlyRootFilesystem: True
      seccompProfile:
        type: RuntimeDefault 
  - name: foobaz
    image: foo/baz:latest
    securityContext:
      privileged: False
      runAsUser: 30000
      runAsGroup: 30000
      readOnlyRootFilesystem: True
  - name: foozed
    image: foo/zed:latest
    securityContext:
      privileged: False
      runAsUser: 30000
      runAsGroup: 30000
      readOnlyRootFilesystem: True
      seccompProfile:
        type: RuntimeDefault 07070100000104000081A4000000000000000000000001680FB1F800000079000000000000000000000000000000000000004000000000kube-score-1.20.0/score/testdata/pod-seccomp-no-annotation.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    07070100000105000081A4000000000000000000000001680FB1F800000136000000000000000000000000000000000000004300000000kube-score-1.20.0/score/testdata/pod-seccomp-securecontext-ok.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  securityContext:
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: foobar
    image: foo/bar:latest
    securityContext:
      privileged: False
      runAsUser: 30000
      runAsGroup: 30000
      readOnlyRootFilesystem: True
07070100000106000081A4000000000000000000000001680FB1F80000011E000000000000000000000000000000000000004800000000kube-score-1.20.0/score/testdata/pod-seccomp-securecontext-warning.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  securityContext:
      privileged: False
      runAsUser: 30000
      runAsGroup: 30000
      readOnlyRootFilesystem: True 
  containers:
  - name: foobar
    image: foo/bar:latest
  - name: foobaz
    image: foo/baz:latest
07070100000107000081A4000000000000000000000001680FB1F8000000F4000000000000000000000000000000000000004400000000kube-score-1.20.0/score/testdata/pod-security-context-all-good.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    securityContext:
      privileged: False
      runAsUser: 30000
      runAsGroup: 30000
      readOnlyRootFilesystem: True
07070100000108000081A4000000000000000000000001680FB1F8000000B8000000000000000000000000000000000000004400000000kube-score-1.20.0/score/testdata/pod-security-context-high-ids.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    securityContext:
      runAsUser: 12000
      runAsGroup: 1300007070100000109000081A4000000000000000000000001680FB1F8000000B4000000000000000000000000000000000000004800000000kube-score-1.20.0/score/testdata/pod-security-context-low-group-id.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    securityContext:
      runAsUser: 15000
      runAsGroup: 00707010000010A000081A4000000000000000000000001680FB1F8000000B4000000000000000000000000000000000000004700000000kube-score-1.20.0/score/testdata/pod-security-context-low-user-id.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    securityContext:
      runAsUser: 0
      runAsGroup: 200000707010000010B000081A4000000000000000000000001680FB1F800000074000000000000000000000000000000000000004D00000000kube-score-1.20.0/score/testdata/pod-security-context-nosecuritycontext.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest0707010000010C000081A4000000000000000000000001680FB1F8000000A0000000000000000000000000000000000000004600000000kube-score-1.20.0/score/testdata/pod-security-context-privileged.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    securityContext:
      privileged: True0707010000010D000081A4000000000000000000000001680FB1F8000000A0000000000000000000000000000000000000005300000000kube-score-1.20.0/score/testdata/pod-security-context-writeablerootfilesystem.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
    securityContext:
      privileged: True0707010000010E000081A4000000000000000000000001680FB1F8000000D9000000000000000000000000000000000000005A00000000kube-score-1.20.0/score/testdata/pod-test-resources-limits-and-requests-no-cpu-limit.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      limits:
        memory: 1Gi
      requests:
        cpu: 200m
        memory: 1Gi
0707010000010F000081A4000000000000000000000001680FB1F8000000EB000000000000000000000000000000000000004D00000000kube-score-1.20.0/score/testdata/pod-test-resources-limits-and-requests.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      limits:
        cpu: 200m
        memory: 1Gi
      requests:
        cpu: 200m
        memory: 1Gi
07070100000110000081A4000000000000000000000001680FB1F80000015A000000000000000000000000000000000000005700000000kube-score-1.20.0/score/testdata/pod-test-resources-no-limits-annotation-optional.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  annotations:
    kube-score/enable: container-memory-requests-equal-limits,  container-cpu-requests-equal-limits    ,   container-resource-requests-equal-limits
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      requests:
        cpu: 200m
        memory: 1Gi
07070100000111000081A4000000000000000000000001680FB1F8000000B7000000000000000000000000000000000000004300000000kube-score-1.20.0/score/testdata/pod-test-resources-no-limits.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      requests:
        cpu: 200m
        memory: 1Gi
07070100000112000081A4000000000000000000000001680FB1F800000072000000000000000000000000000000000000003E00000000kube-score-1.20.0/score/testdata/pod-test-resources-none.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
07070100000113000081A4000000000000000000000001680FB1F8000000B5000000000000000000000000000000000000004500000000kube-score-1.20.0/score/testdata/pod-test-resources-only-limits.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
spec:
  containers:
  - name: foobar
    image: foo/bar:123
    resources:
      limits:
        cpu: 200m
        memory: 1Gi
07070100000114000081A4000000000000000000000001680FB1F800000133000000000000000000000000000000000000005600000000kube-score-1.20.0/score/testdata/pod-topology-spread-constraints-invalid-maxskew.yamlkind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 0
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
    - name: pause
      image: registry.k8s.io/pause:3.1
07070100000115000081A4000000000000000000000001680FB1F800000145000000000000000000000000000000000000005900000000kube-score-1.20.0/score/testdata/pod-topology-spread-constraints-invalid-mindomains.yamlkind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    minDomains: 0
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
    - name: pause
      image: registry.k8s.io/pause:3.1
07070100000116000081A4000000000000000000000001680FB1F800000134000000000000000000000000000000000000006000000000kube-score-1.20.0/score/testdata/pod-topology-spread-constraints-invalid-whenunsatisfiable.yamlkind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSCHEDULE 
    labelSelector:
      matchLabels:
        foo: bar
  containers:
    - name: pause
      image: registry.k8s.io/pause:3.1
07070100000117000081A4000000000000000000000001680FB1F8000000FC000000000000000000000000000000000000005700000000kube-score-1.20.0/score/testdata/pod-topology-spread-constraints-no-labelselector.yamlkind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
  containers:
    - name: pause
      image: registry.k8s.io/pause:3.1
07070100000118000081A4000000000000000000000001680FB1F80000011D000000000000000000000000000000000000005500000000kube-score-1.20.0/score/testdata/pod-topology-spread-constraints-no-topologykey.yamlkind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
    - name: pause
      image: registry.k8s.io/pause:3.1
07070100000119000081A4000000000000000000000001680FB1F800000133000000000000000000000000000000000000005500000000kube-score-1.20.0/score/testdata/pod-topology-spread-constraints-one-constraint.yamlkind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
    - name: pause
      image: registry.k8s.io/pause:3.1
0707010000011A000081A4000000000000000000000001680FB1F8000001AD000000000000000000000000000000000000005600000000kube-score-1.20.0/score/testdata/pod-topology-spread-constraints-two-constraints.yamlkind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  - maxSkew: 1
    topologyKey: node
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
  - name: pause
  image: registry.k8s.io/pause:3.10707010000011B000081A4000000000000000000000001680FB1F80000019D000000000000000000000000000000000000004C00000000kube-score-1.20.0/score/testdata/security-inherit-pod-security-context.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: test
  labels:
    app: test
spec:
  selector:
    matchLabels:
      app: test
  template:
    metadata:
      labels:
        app: test
    spec:
      securityContext:
        runAsGroup: 20000
        runAsUser: 20000
      containers:
        - name: test
          securityContext:
            privileged: false
            readOnlyRootFilesystem: true0707010000011C000081A4000000000000000000000001680FB1F8000000CD000000000000000000000000000000000000003B00000000kube-score-1.20.0/score/testdata/service-externalname.yamlapiVersion: v1
kind: Service
metadata:
  name: wp-site
  namespace: site
spec:
  externalName: wp.example.com
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  type: ExternalName
0707010000011D000081A4000000000000000000000001680FB1F800000186000000000000000000000000000000000000004400000000kube-score-1.20.0/score/testdata/service-not-target-deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-test-1
spec:
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
---
kind: Service
apiVersion: v1
metadata:
  name: my-service-no-match
spec:
  selector:
    app: my-app-no-match
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
0707010000011E000081A4000000000000000000000001680FB1F800000153000000000000000000000000000000000000004900000000kube-score-1.20.0/score/testdata/service-not-target-pod-multi-label.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: my-app
    foo: bar
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app
    foo: bar-not-matching
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
0707010000011F000081A4000000000000000000000001680FB1F800000139000000000000000000000000000000000000003D00000000kube-score-1.20.0/score/testdata/service-not-target-pod.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: my-app
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app-not-matching
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
07070100000120000081A4000000000000000000000001680FB1F8000001A4000000000000000000000000000000000000005400000000kube-score-1.20.0/score/testdata/service-target-deployment-different-namespace.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-test-1
  namespace: foospace
spec:
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: not-foospace
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
07070100000121000081A4000000000000000000000001680FB1F800000193000000000000000000000000000000000000004C00000000kube-score-1.20.0/score/testdata/service-target-deployment-not-rolling.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-test-1
spec:
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
  strategy:
    type: Recreate
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
07070100000122000081A4000000000000000000000001680FB1F8000001A6000000000000000000000000000000000000004A00000000kube-score-1.20.0/score/testdata/service-target-deployment-replica-1.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-test-1
spec:
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
  replicas: 1
  strategy:
    type: RollingUpdate
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
07070100000123000081A4000000000000000000000001680FB1F8000001A0000000000000000000000000000000000000004F00000000kube-score-1.20.0/score/testdata/service-target-deployment-same-namespace.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-test-1
  namespace: foospace
spec:
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: foospace
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
07070100000124000081A4000000000000000000000001680FB1F800000174000000000000000000000000000000000000005100000000kube-score-1.20.0/score/testdata/service-target-deployment-strategy-not-set.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-test-1
spec:
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
07070100000125000081A4000000000000000000000001680FB1F8000001A6000000000000000000000000000000000000004000000000kube-score-1.20.0/score/testdata/service-target-deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-test-1
spec:
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
  replicas: 2
  strategy:
    type: RollingUpdate
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
07070100000126000081A4000000000000000000000001680FB1F80000015C000000000000000000000000000000000000004D00000000kube-score-1.20.0/score/testdata/service-target-pod-different-namespace.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: foospace
  labels:
    app: my-app
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: not-foospace
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
07070100000127000081A4000000000000000000000001680FB1F800000146000000000000000000000000000000000000004500000000kube-score-1.20.0/score/testdata/service-target-pod-multi-label.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: my-app
    foo: bar
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app
    foo: bar
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
07070100000128000081A4000000000000000000000001680FB1F800000158000000000000000000000000000000000000004800000000kube-score-1.20.0/score/testdata/service-target-pod-same-namespace.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  namespace: foospace
  labels:
    app: my-app
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: foospace
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
07070100000129000081A4000000000000000000000001680FB1F80000012C000000000000000000000000000000000000003900000000kube-score-1.20.0/score/testdata/service-target-pod.yamlapiVersion: v1
kind: Pod
metadata:
  name: pod-test-1
  labels:
    app: my-app
spec:
  containers:
  - name: foobar
    image: foo/bar:latest
---
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
0707010000012A000081A4000000000000000000000001680FB1F8000000C8000000000000000000000000000000000000003D00000000kube-score-1.20.0/score/testdata/service-type-clusterip.yamlapiVersion: v1
kind: Service
metadata:
  name: node-port-service
  namespace: foospace
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: ClusterIP
0707010000012B000081A4000000000000000000000001680FB1F8000000B6000000000000000000000000000000000000003B00000000kube-score-1.20.0/score/testdata/service-type-default.yamlapiVersion: v1
kind: Service
metadata:
  name: node-port-service
  namespace: foospace
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
0707010000012C000081A4000000000000000000000001680FB1F8000000C7000000000000000000000000000000000000003C00000000kube-score-1.20.0/score/testdata/service-type-nodeport.yamlapiVersion: v1
kind: Service
metadata:
  name: node-port-service
  namespace: foospace
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
  type: NodePort
0707010000012D000081A4000000000000000000000001680FB1F80000012D000000000000000000000000000000000000003E00000000kube-score-1.20.0/score/testdata/statefulset-appsv1beta1.yamlapiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  template:
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
        resources:
          limits:
            cpu: 200m
            memory: 1Gi
0707010000012E000081A4000000000000000000000001680FB1F80000012D000000000000000000000000000000000000003E00000000kube-score-1.20.0/score/testdata/statefulset-appsv1beta2.yamlapiVersion: apps/v1beta2
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  template:
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
        resources:
          limits:
            cpu: 200m
            memory: 1Gi
0707010000012F000081A4000000000000000000000001680FB1F80000011C000000000000000000000000000000000000004300000000kube-score-1.20.0/score/testdata/statefulset-different-labels.yamlapiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  serviceName: svc-test-1
  template:
    metadata:
      labels:
        app: bar
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
07070100000130000081A4000000000000000000000001680FB1F800000121000000000000000000000000000000000000004E00000000kube-score-1.20.0/score/testdata/statefulset-host-antiaffinity-1-replica.yamlapiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-host-antiaffinity-1-replica
spec:
  selector:
    matchLabels:
      app: foo
  replicas: 1
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar
07070100000131000081A4000000000000000000000001680FB1F80000011C000000000000000000000000000000000000004C00000000kube-score-1.20.0/score/testdata/statefulset-host-antiaffinity-not-set.yamlapiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-anti-affinity-not-set
spec:
  selector:
    matchLabels:
      app: foo
  replicas: 10
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar
07070100000132000081A4000000000000000000000001680FB1F800000294000000000000000000000000000000000000004E00000000kube-score-1.20.0/score/testdata/statefulset-host-antiaffinity-preffered.yamlapiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-preffered-anti-affinity
spec:
  selector:
    matchLabels:
      app: foo
  replicas: 10
  template:
    metadata:
      labels:
        app: foo
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - foo
              topologyKey: kubernetes.io/hostname
      containers:
      - name: foobar
        image: foo:bar
07070100000133000081A4000000000000000000000001680FB1F800000217000000000000000000000000000000000000004D00000000kube-score-1.20.0/score/testdata/statefulset-host-antiaffinity-required.yamlapiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-required-anti-affinity
spec:
  selector:
    matchLabels:
      app: foo
  replicas: 10
  template:
    metadata:
      labels:
        app: foo
    spec:
      replicas: 10
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
           - topologyKey: "kubernetes.io/hostname"
             labelSelector:
               matchLabels:
                app: foo
      containers:
      - name: foobar
        image: foo:bar
07070100000134000081A4000000000000000000000001680FB1F80000011C000000000000000000000000000000000000005700000000kube-score-1.20.0/score/testdata/statefulset-host-antiaffinity-undefined-replicas.yamlapiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-host-antiaffinity-undefined-replicas
spec:
  selector:
    matchLabels:
      app: foo
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar
07070100000135000081A4000000000000000000000001680FB1F800000B36000000000000000000000000000000000000004100000000kube-score-1.20.0/score/testdata/statefulset-nested-ignores.yamlapiVersion: apps/v1
kind: StatefulSet
metadata:
  name: trivy
  namespace: trivy-staging
spec:
  podManagementPolicy: Parallel
  replicas: 3
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app.kubernetes.io/instance: trivy
      app.kubernetes.io/name: trivy
  serviceName: trivy
  template:
    metadata:
      annotations:
        kube-score/ignore: container-image-tag,pod-probes
      labels:
        app.kubernetes.io/instance: trivy
        app.kubernetes.io/name: trivy
    spec:
      automountServiceAccountToken: false
      containers:
        - args:
            - server
          envFrom:
            - configMapRef:
                name: trivy
            - secretRef:
                name: trivy
          image: aquasec/trivy:latest
          imagePullPolicy: Always
          livenessProbe:
            failureThreshold: 10
            httpGet:
              path: /healthz
              port: trivy-http
              scheme: HTTP
            initialDelaySeconds: 5
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          name: main
          ports:
            - containerPort: 4954
              name: trivy-http
              protocol: TCP
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: trivy-http
              scheme: HTTP
            initialDelaySeconds: 5
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
          resources:
            limits:
              cpu: "1"
              ephemeral-storage: 128Mi
              memory: 1Gi
            requests:
              cpu: 200m
              memory: 512Mi
          securityContext:
            privileged: false
            readOnlyRootFilesystem: true
            runAsGroup: 65534
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
          volumeMounts:
            - mountPath: /tmp
              name: tmp-data
            - mountPath: /home/scanner/.cache
              name: data
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext:
        fsGroup: 65534
        runAsNonRoot: true
        runAsUser: 65534
      serviceAccount: trivy
      serviceAccountName: trivy
      terminationGracePeriodSeconds: 30
      volumes:
        - emptyDir: {}
          name: tmp-data
  updateStrategy:
    rollingUpdate:
      partition: 0
    type: RollingUpdate
  volumeClaimTemplates:
    - apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        creationTimestamp: null
        name: data
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 5Gi
        volumeMode: Filesystem
      status:
        phase: Pending07070100000136000081A4000000000000000000000001680FB1F8000001D0000000000000000000000000000000000000006100000000kube-score-1.20.0/score/testdata/statefulset-poddisruptionbudget-v1beta1-expression-matches.yamlapiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  minAvailable: 2
  selector:
    matchExpressions:
    - key: app
      operator: In
      values: [foo, foo1, foo2]
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar
07070100000137000081A4000000000000000000000001680FB1F8000001D6000000000000000000000000000000000000006200000000kube-score-1.20.0/score/testdata/statefulset-poddisruptionbudget-v1beta1-expression-no-match.yamlapiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  minAvailable: 2
  selector:
    matchExpressions:
    - key: app
      operator: In
      values: ["not-foo", "not-foo2"]
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar
07070100000138000081A4000000000000000000000001680FB1F800000198000000000000000000000000000000000000005600000000kube-score-1.20.0/score/testdata/statefulset-poddisruptionbudget-v1beta1-matches.yamlapiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: foo
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar
07070100000139000081A4000000000000000000000001680FB1F80000019C000000000000000000000000000000000000005700000000kube-score-1.20.0/score/testdata/statefulset-poddisruptionbudget-v1beta1-no-match.yamlapiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: not-foo
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar
0707010000013A000081A4000000000000000000000001680FB1F8000001CA000000000000000000000000000000000000004F00000000kube-score-1.20.0/score/testdata/statefulset-service-name-different-label.yamlapiVersion: v1
kind: Service
metadata:
  name: svc-test-1
spec:
  clusterIP: "None"
  selector:
    app: foo
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  serviceName: svc-test-1
  template:
    metadata:
      labels:
        app: bar
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
0707010000013B000081A4000000000000000000000001680FB1F8000001CA000000000000000000000000000000000000004E00000000kube-score-1.20.0/score/testdata/statefulset-service-name-different-name.yamlapiVersion: v1
kind: Service
metadata:
  name: svc-test-2
spec:
  clusterIP: "None"
  selector:
    app: foo
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  serviceName: svc-test-1
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
0707010000013C000081A4000000000000000000000001680FB1F8000001E2000000000000000000000000000000000000005300000000kube-score-1.20.0/score/testdata/statefulset-service-name-different-namespace.yaml
apiVersion: v1
kind: Service
metadata:
  name: svc-test-1
spec:
  clusterIP: "None"
  selector:
    app: foo
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
  namespace: ns-test-1
spec:
  selector:
    matchLabels:
      app: foo
  serviceName: svc-test-1
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
0707010000013D000081A4000000000000000000000001680FB1F8000001B6000000000000000000000000000000000000004C00000000kube-score-1.20.0/score/testdata/statefulset-service-name-not-headless.yamlapiVersion: v1
kind: Service
metadata:
  name: svc-test-1
spec:
  selector:
    app: foo
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  serviceName: svc-test-1
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
0707010000013E000081A4000000000000000000000001680FB1F8000001CA000000000000000000000000000000000000003F00000000kube-score-1.20.0/score/testdata/statefulset-service-name.yamlapiVersion: v1
kind: Service
metadata:
  name: svc-test-1
spec:
  clusterIP: "None"
  selector:
    app: foo
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  serviceName: svc-test-1
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
0707010000013F000081A4000000000000000000000001680FB1F800000128000000000000000000000000000000000000004100000000kube-score-1.20.0/score/testdata/statefulset-test-resources.yamlapiVersion: apps/v1
kind: StatefulSet
metadata:
  name: statefulset-test-1
spec:
  selector:
    matchLabels:
      app: foo
  template:
    spec:
      containers:
      - name: foobar
        image: foo/bar:123
        resources:
          limits:
            cpu: 200m
            memory: 1Gi
07070100000140000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001C00000000kube-score-1.20.0/scorecard07070100000141000081A4000000000000000000000001680FB1F800000501000000000000000000000000000000000000002700000000kube-score-1.20.0/scorecard/enabled.gopackage scorecard

import (
	"strings"

	ks "github.com/zegl/kube-score/domain"
)

func (so *ScoredObject) isEnabled(check ks.Check, annotations, childAnnotations map[string]string) bool {
	isIn := func(csv string, key string) bool {
		for _, v := range strings.Split(csv, ",") {
			v = strings.TrimSpace(v)
			if v == key {
				return true
			}
			if vals, ok := impliedIgnoreAnnotations[v]; ok {
				for i := range vals {
					if vals[i] == key {
						return true
					}
				}
			}
		}
		return false
	}

	if childAnnotations != nil && so.useIgnoreChecksAnnotation && isIn(childAnnotations[ignoredChecksAnnotation], check.ID) {
		return false
	}
	if childAnnotations != nil && so.useOptionalChecksAnnotation && isIn(childAnnotations[optionalChecksAnnotation], check.ID) {
		return true
	}
	if so.useIgnoreChecksAnnotation && isIn(annotations[ignoredChecksAnnotation], check.ID) {
		return false
	}
	if so.useOptionalChecksAnnotation && isIn(annotations[optionalChecksAnnotation], check.ID) {
		return true
	}

	// Enabled optional test from command line arguments
	if _, ok := so.enabledOptionalTests[check.ID]; ok {
		return true
	}

	// Optional checks are disabled unless explicitly allowed above
	if check.Optional {
		return false
	}

	// Enabled by default
	return true
}
07070100000142000081A4000000000000000000000001680FB1F800000F74000000000000000000000000000000000000002900000000kube-score-1.20.0/scorecard/scorecard.gopackage scorecard

import (
	"fmt"

	"github.com/zegl/kube-score/config"
	ks "github.com/zegl/kube-score/domain"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	ignoredChecksAnnotation  = "kube-score/ignore"
	optionalChecksAnnotation = "kube-score/enable"
)

// if this, then that
var impliedIgnoreAnnotations = map[string][]string{
	"container-resources": {"container-ephemeral-storage-request-and-limit"},
}

type Scorecard map[string]*ScoredObject

// New creates and initializes a new Scorecard
func New() Scorecard {
	return make(Scorecard)
}

func (s Scorecard) NewObject(typeMeta metav1.TypeMeta, objectMeta metav1.ObjectMeta, cnf *config.RunConfiguration) *ScoredObject {
	if cnf == nil {
		cnf = &config.RunConfiguration{}
	}

	o := &ScoredObject{
		TypeMeta:   typeMeta,
		ObjectMeta: objectMeta,
		Checks:     make([]TestScore, 0),

		useIgnoreChecksAnnotation:   cnf.UseIgnoreChecksAnnotation,
		useOptionalChecksAnnotation: cnf.UseOptionalChecksAnnotation,
		enabledOptionalTests:        cnf.EnabledOptionalTests,
	}

	// If this object already exists, return the previous version
	if object, ok := s[o.resourceRefKey()]; ok {
		return object
	}

	s[o.resourceRefKey()] = o
	return o
}

func (s Scorecard) AnyBelowOrEqualToGrade(threshold Grade) bool {
	for _, o := range s {
		if o.AnyBelowOrEqualToGrade(threshold) {
			return true
		}
	}
	return false
}

type ScoredObject struct {
	TypeMeta     metav1.TypeMeta
	ObjectMeta   metav1.ObjectMeta
	FileLocation ks.FileLocation
	Checks       []TestScore

	useIgnoreChecksAnnotation   bool
	useOptionalChecksAnnotation bool
	enabledOptionalTests        map[string]struct{}
}

func (so *ScoredObject) AnyBelowOrEqualToGrade(threshold Grade) bool {
	for _, o := range so.Checks {
		if !o.Skipped && o.Grade <= threshold {
			return true
		}
	}
	return false
}

func (so *ScoredObject) resourceRefKey() string {
	return so.TypeMeta.Kind + "/" + so.TypeMeta.APIVersion + "/" + so.ObjectMeta.Namespace + "/" + so.ObjectMeta.Name
}

func (so *ScoredObject) HumanFriendlyRef() string {
	s := so.ObjectMeta.Name
	if so.ObjectMeta.Namespace != "" {
		s += "/" + so.ObjectMeta.Namespace
	}
	s += " " + so.TypeMeta.APIVersion + "/" + so.TypeMeta.Kind
	return s
}

func (so *ScoredObject) Add(ts TestScore, check ks.Check, locationer ks.FileLocationer, annotations ...map[string]string) {
	ts.Check = check
	so.FileLocation = locationer.FileLocation()

	var skip bool
	if annotations != nil {
		if len(annotations) == 1 && !so.isEnabled(check, annotations[0], nil) {
			skip = true
		}
		if len(annotations) == 2 && !so.isEnabled(check, annotations[0], annotations[1]) {
			skip = true
		}
	}

	// This test is ignored (via annotations), don't save the score
	if skip {
		ts.Skipped = true
		ts.Comments = []TestScoreComment{{Summary: fmt.Sprintf("Skipped because %s is ignored", check.ID)}}
	}

	so.Checks = append(so.Checks, ts)
}

type TestScore struct {
	Check    ks.Check
	Grade    Grade
	Skipped  bool
	Comments []TestScoreComment
}

type Grade int

const (
	GradeCritical Grade = 1
	GradeWarning  Grade = 5
	GradeAlmostOK Grade = 7
	GradeAllOK    Grade = 10
)

func (g Grade) String() string {
	switch g {
	case GradeCritical:
		return "CRITICAL"
	case GradeWarning:
		return "WARNING"
	case GradeAlmostOK, GradeAllOK:
		return "OK"
	default:
		panic("Unknown grade")
	}
}

type TestScoreComment struct {
	Path             string
	Summary          string
	Description      string
	DocumentationURL string
}

func (ts *TestScore) AddComment(path, summary, description string) {
	ts.Comments = append(ts.Comments, TestScoreComment{
		Path:        path,
		Summary:     summary,
		Description: description,
	})
}

func (ts *TestScore) AddCommentWithURL(path, summary, description, documentationURL string) {
	ts.Comments = append(ts.Comments, TestScoreComment{
		Path:             path,
		Summary:          summary,
		Description:      description,
		DocumentationURL: documentationURL,
	})
}
07070100000143000081A4000000000000000000000001680FB1F80000007E000000000000000000000000000000000000001B00000000kube-score-1.20.0/tea.yaml# https://tea.xyz/what-is-this-file
---
version: 1.0.0
codeOwners:
  - '0xf731ba6c4D26eBcD80793f41d7aedd64fe94E724'
quorum: 1
07070100000144000081A4000000000000000000000001680FB1F80000019A000000000000000000000000000000000000001E00000000kube-score-1.20.0/vercel.json{
    "buildCommand": "GOOS=js GOARCH=wasm /vercel/go/bin/go1.22.5 build -o ./web/public/main.wasm ./cmd/wasm/main.go && cd web && ENABLE_EXPERIMENTAL_COREPACK=1 pnpm build",
    "installCommand": "cd web && ENABLE_EXPERIMENTAL_COREPACK=1 pnpm install --frozen-lockfile && dnf install -y go && go install golang.org/dl/go1.22.5@latest && /vercel/go/bin/go1.22.5 download",
    "outputDirectory": "./web/dist"
}07070100000145000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001600000000kube-score-1.20.0/web07070100000146000081A4000000000000000000000001680FB1F8000001B4000000000000000000000000000000000000002400000000kube-score-1.20.0/web/.eslintrc.cjsmodule.exports = {
  root: true,
  env: { browser: true, es2020: true },
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react-hooks/recommended',
  ],
  ignorePatterns: ['dist', '.eslintrc.cjs'],
  parser: '@typescript-eslint/parser',
  plugins: ['react-refresh'],
  rules: {
    'react-refresh/only-export-components': [
      'warn',
      { allowConstantExport: true },
    ],
  },
}
07070100000147000081A4000000000000000000000001680FB1F80000010B000000000000000000000000000000000000002100000000kube-score-1.20.0/web/.gitignore*.wasm
.vite

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
07070100000148000081A4000000000000000000000001680FB1F800000207000000000000000000000000000000000000002000000000kube-score-1.20.0/web/README.md# The kube-score website

This directory contains the source for kube-score.com.

kube-score is compiled to WASM (WebAssembly) for usage in browsers, and kube-score.com is running purely in the browser.

## Building

```bash
# Compile the wasm module (run from repo root)
GOOS=js GOARCH=wasm go build -o ./web/main.wasm ./cmd/wasm/main.go
```

```bash
# Build and run web app (from this directory)
pnpm install
pnpm dev
```

## Hosting

The site is built and hosted on Vercel. See vercel.json for build configuration.

07070100000149000081A4000000000000000000000001680FB1F8000003B8000000000000000000000000000000000000002100000000kube-score-1.20.0/web/index.html<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/assets/logo.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>kube-score - Kubernetes object analysis with recommendations for improved reliability and security</title>
    <meta name="description"
        content="kube-score is a tool that does static code analysis of your Kubernetes object definitions. The output is a list of recommendations of what you can improve to make your application more secure and resilient.">
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
    <script src="wasm_exec.js"></script>
    <script>
        const go = new Go();
        WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
            go.run(result.instance);
        });
    </script>
  </body>
</html>
0707010000014A000081A4000000000000000000000001680FB1F80000030C000000000000000000000000000000000000002300000000kube-score-1.20.0/web/package.json{
  "name": "web",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "styled-components": "^6.1.11"
  },
  "devDependencies": {
    "@types/react": "^18.3.3",
    "@types/react-dom": "^18.3.0",
    "@typescript-eslint/eslint-plugin": "^8.8.1",
    "@typescript-eslint/parser": "^8.8.1",
    "@vitejs/plugin-react-swc": "^3.7.1",
    "eslint": "^9.12.0",
    "eslint-plugin-react-hooks": "^5.0.0",
    "eslint-plugin-react-refresh": "^0.4.12",
    "typescript": "^5.6.3",
    "vite": "^5.4.8"
  }
}
0707010000014B000081A4000000000000000000000001680FB1F80000EA7E000000000000000000000000000000000000002500000000kube-score-1.20.0/web/pnpm-lock.yamllockfileVersion: '9.0'

settings:
  autoInstallPeers: true
  excludeLinksFromLockfile: false

importers:

  .:
    dependencies:
      react:
        specifier: ^18.3.1
        version: 18.3.1
      react-dom:
        specifier: ^18.3.1
        version: 18.3.1(react@18.3.1)
      styled-components:
        specifier: ^6.1.11
        version: 6.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
    devDependencies:
      '@types/react':
        specifier: ^18.3.3
        version: 18.3.3
      '@types/react-dom':
        specifier: ^18.3.0
        version: 18.3.0
      '@typescript-eslint/eslint-plugin':
        specifier: ^7.13.1
        version: 7.16.0(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)
      '@typescript-eslint/parser':
        specifier: ^7.13.1
        version: 7.16.0(eslint@8.57.0)(typescript@5.5.3)
      '@vitejs/plugin-react-swc':
        specifier: ^3.5.0
        version: 3.7.0(vite@5.3.3)
      eslint:
        specifier: ^8.57.0
        version: 8.57.0
      eslint-plugin-react-hooks:
        specifier: ^4.6.2
        version: 4.6.2(eslint@8.57.0)
      eslint-plugin-react-refresh:
        specifier: ^0.4.7
        version: 0.4.8(eslint@8.57.0)
      typescript:
        specifier: ^5.2.2
        version: 5.5.3
      vite:
        specifier: ^5.3.1
        version: 5.3.3

packages:

  '@emotion/is-prop-valid@1.2.2':
    resolution: {integrity: sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==}

  '@emotion/memoize@0.8.1':
    resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==}

  '@emotion/unitless@0.8.1':
    resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}

  '@esbuild/aix-ppc64@0.21.5':
    resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
    engines: {node: '>=12'}
    cpu: [ppc64]
    os: [aix]

  '@esbuild/android-arm64@0.21.5':
    resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [android]

  '@esbuild/android-arm@0.21.5':
    resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
    engines: {node: '>=12'}
    cpu: [arm]
    os: [android]

  '@esbuild/android-x64@0.21.5':
    resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [android]

  '@esbuild/darwin-arm64@0.21.5':
    resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [darwin]

  '@esbuild/darwin-x64@0.21.5':
    resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [darwin]

  '@esbuild/freebsd-arm64@0.21.5':
    resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [freebsd]

  '@esbuild/freebsd-x64@0.21.5':
    resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [freebsd]

  '@esbuild/linux-arm64@0.21.5':
    resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [linux]

  '@esbuild/linux-arm@0.21.5':
    resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
    engines: {node: '>=12'}
    cpu: [arm]
    os: [linux]

  '@esbuild/linux-ia32@0.21.5':
    resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
    engines: {node: '>=12'}
    cpu: [ia32]
    os: [linux]

  '@esbuild/linux-loong64@0.21.5':
    resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
    engines: {node: '>=12'}
    cpu: [loong64]
    os: [linux]

  '@esbuild/linux-mips64el@0.21.5':
    resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
    engines: {node: '>=12'}
    cpu: [mips64el]
    os: [linux]

  '@esbuild/linux-ppc64@0.21.5':
    resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
    engines: {node: '>=12'}
    cpu: [ppc64]
    os: [linux]

  '@esbuild/linux-riscv64@0.21.5':
    resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
    engines: {node: '>=12'}
    cpu: [riscv64]
    os: [linux]

  '@esbuild/linux-s390x@0.21.5':
    resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
    engines: {node: '>=12'}
    cpu: [s390x]
    os: [linux]

  '@esbuild/linux-x64@0.21.5':
    resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [linux]

  '@esbuild/netbsd-x64@0.21.5':
    resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [netbsd]

  '@esbuild/openbsd-x64@0.21.5':
    resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [openbsd]

  '@esbuild/sunos-x64@0.21.5':
    resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [sunos]

  '@esbuild/win32-arm64@0.21.5':
    resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [win32]

  '@esbuild/win32-ia32@0.21.5':
    resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
    engines: {node: '>=12'}
    cpu: [ia32]
    os: [win32]

  '@esbuild/win32-x64@0.21.5':
    resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [win32]

  '@eslint-community/eslint-utils@4.4.0':
    resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
    peerDependencies:
      eslint: ^6.0.0 || ^7.0.0 || >=8.0.0

  '@eslint-community/regexpp@4.11.0':
    resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==}
    engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}

  '@eslint/eslintrc@2.1.4':
    resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}

  '@eslint/js@8.57.0':
    resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==}
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}

  '@humanwhocodes/config-array@0.11.14':
    resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
    engines: {node: '>=10.10.0'}
    deprecated: Use @eslint/config-array instead

  '@humanwhocodes/module-importer@1.0.1':
    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
    engines: {node: '>=12.22'}

  '@humanwhocodes/object-schema@2.0.3':
    resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==}
    deprecated: Use @eslint/object-schema instead

  '@nodelib/fs.scandir@2.1.5':
    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
    engines: {node: '>= 8'}

  '@nodelib/fs.stat@2.0.5':
    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
    engines: {node: '>= 8'}

  '@nodelib/fs.walk@1.2.8':
    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
    engines: {node: '>= 8'}

  '@rollup/rollup-android-arm-eabi@4.18.1':
    resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==}
    cpu: [arm]
    os: [android]

  '@rollup/rollup-android-arm64@4.18.1':
    resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==}
    cpu: [arm64]
    os: [android]

  '@rollup/rollup-darwin-arm64@4.18.1':
    resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==}
    cpu: [arm64]
    os: [darwin]

  '@rollup/rollup-darwin-x64@4.18.1':
    resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==}
    cpu: [x64]
    os: [darwin]

  '@rollup/rollup-linux-arm-gnueabihf@4.18.1':
    resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==}
    cpu: [arm]
    os: [linux]

  '@rollup/rollup-linux-arm-musleabihf@4.18.1':
    resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==}
    cpu: [arm]
    os: [linux]

  '@rollup/rollup-linux-arm64-gnu@4.18.1':
    resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==}
    cpu: [arm64]
    os: [linux]

  '@rollup/rollup-linux-arm64-musl@4.18.1':
    resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==}
    cpu: [arm64]
    os: [linux]

  '@rollup/rollup-linux-powerpc64le-gnu@4.18.1':
    resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==}
    cpu: [ppc64]
    os: [linux]

  '@rollup/rollup-linux-riscv64-gnu@4.18.1':
    resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==}
    cpu: [riscv64]
    os: [linux]

  '@rollup/rollup-linux-s390x-gnu@4.18.1':
    resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==}
    cpu: [s390x]
    os: [linux]

  '@rollup/rollup-linux-x64-gnu@4.18.1':
    resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==}
    cpu: [x64]
    os: [linux]

  '@rollup/rollup-linux-x64-musl@4.18.1':
    resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==}
    cpu: [x64]
    os: [linux]

  '@rollup/rollup-win32-arm64-msvc@4.18.1':
    resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==}
    cpu: [arm64]
    os: [win32]

  '@rollup/rollup-win32-ia32-msvc@4.18.1':
    resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==}
    cpu: [ia32]
    os: [win32]

  '@rollup/rollup-win32-x64-msvc@4.18.1':
    resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==}
    cpu: [x64]
    os: [win32]

  '@swc/core-darwin-arm64@1.6.13':
    resolution: {integrity: sha512-SOF4buAis72K22BGJ3N8y88mLNfxLNprTuJUpzikyMGrvkuBFNcxYtMhmomO0XHsgLDzOJ+hWzcgjRNzjMsUcQ==}
    engines: {node: '>=10'}
    cpu: [arm64]
    os: [darwin]

  '@swc/core-darwin-x64@1.6.13':
    resolution: {integrity: sha512-AW8akFSC+tmPE6YQQvK9S2A1B8pjnXEINg+gGgw0KRUUXunvu1/OEOeC5L2Co1wAwhD7bhnaefi06Qi9AiwOag==}
    engines: {node: '>=10'}
    cpu: [x64]
    os: [darwin]

  '@swc/core-linux-arm-gnueabihf@1.6.13':
    resolution: {integrity: sha512-f4gxxvDXVUm2HLYXRd311mSrmbpQF2MZ4Ja6XCQz1hWAxXdhRl1gpnZ+LH/xIfGSwQChrtLLVrkxdYUCVuIjFg==}
    engines: {node: '>=10'}
    cpu: [arm]
    os: [linux]

  '@swc/core-linux-arm64-gnu@1.6.13':
    resolution: {integrity: sha512-Nf/eoW2CbG8s+9JoLtjl9FByBXyQ5cjdBsA4efO7Zw4p+YSuXDgc8HRPC+E2+ns0praDpKNZtLvDtmF2lL+2Gg==}
    engines: {node: '>=10'}
    cpu: [arm64]
    os: [linux]

  '@swc/core-linux-arm64-musl@1.6.13':
    resolution: {integrity: sha512-2OysYSYtdw79prJYuKIiux/Gj0iaGEbpS2QZWCIY4X9sGoETJ5iMg+lY+YCrIxdkkNYd7OhIbXdYFyGs/w5LDg==}
    engines: {node: '>=10'}
    cpu: [arm64]
    os: [linux]

  '@swc/core-linux-x64-gnu@1.6.13':
    resolution: {integrity: sha512-PkR4CZYJNk5hcd2+tMWBpnisnmYsUzazI1O5X7VkIGFcGePTqJ/bWlfUIVVExWxvAI33PQFzLbzmN5scyIUyGQ==}
    engines: {node: '>=10'}
    cpu: [x64]
    os: [linux]

  '@swc/core-linux-x64-musl@1.6.13':
    resolution: {integrity: sha512-OdsY7wryTxCKwGQcwW9jwWg3cxaHBkTTHi91+5nm7hFPpmZMz1HivJrWAMwVE7iXFw+M4l6ugB/wCvpYrUAAjA==}
    engines: {node: '>=10'}
    cpu: [x64]
    os: [linux]

  '@swc/core-win32-arm64-msvc@1.6.13':
    resolution: {integrity: sha512-ap6uNmYjwk9M/+bFEuWRNl3hq4VqgQ/Lk+ID/F5WGqczNr0L7vEf+pOsRAn0F6EV+o/nyb3ePt8rLhE/wjHpPg==}
    engines: {node: '>=10'}
    cpu: [arm64]
    os: [win32]

  '@swc/core-win32-ia32-msvc@1.6.13':
    resolution: {integrity: sha512-IJ8KH4yIUHTnS/U1jwQmtbfQals7zWPG0a9hbEfIr4zI0yKzjd83lmtS09lm2Q24QBWOCFGEEbuZxR4tIlvfzA==}
    engines: {node: '>=10'}
    cpu: [ia32]
    os: [win32]

  '@swc/core-win32-x64-msvc@1.6.13':
    resolution: {integrity: sha512-f6/sx6LMuEnbuxtiSL/EkR0Y6qUHFw1XVrh6rwzKXptTipUdOY+nXpKoh+1UsBm/r7H0/5DtOdrn3q5ZHbFZjQ==}
    engines: {node: '>=10'}
    cpu: [x64]
    os: [win32]

  '@swc/core@1.6.13':
    resolution: {integrity: sha512-eailUYex6fkfaQTev4Oa3mwn0/e3mQU4H8y1WPuImYQESOQDtVrowwUGDSc19evpBbHpKtwM+hw8nLlhIsF+Tw==}
    engines: {node: '>=10'}
    peerDependencies:
      '@swc/helpers': '*'
    peerDependenciesMeta:
      '@swc/helpers':
        optional: true

  '@swc/counter@0.1.3':
    resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}

  '@swc/types@0.1.9':
    resolution: {integrity: sha512-qKnCno++jzcJ4lM4NTfYifm1EFSCeIfKiAHAfkENZAV5Kl9PjJIyd2yeeVv6c/2CckuLyv2NmRC5pv6pm2WQBg==}

  '@types/estree@1.0.5':
    resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}

  '@types/prop-types@15.7.12':
    resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}

  '@types/react-dom@18.3.0':
    resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==}

  '@types/react@18.3.3':
    resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==}

  '@types/stylis@4.2.5':
    resolution: {integrity: sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==}

  '@typescript-eslint/eslint-plugin@7.16.0':
    resolution: {integrity: sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==}
    engines: {node: ^18.18.0 || >=20.0.0}
    peerDependencies:
      '@typescript-eslint/parser': ^7.0.0
      eslint: ^8.56.0
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true

  '@typescript-eslint/parser@7.16.0':
    resolution: {integrity: sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==}
    engines: {node: ^18.18.0 || >=20.0.0}
    peerDependencies:
      eslint: ^8.56.0
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true

  '@typescript-eslint/scope-manager@7.16.0':
    resolution: {integrity: sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==}
    engines: {node: ^18.18.0 || >=20.0.0}

  '@typescript-eslint/type-utils@7.16.0':
    resolution: {integrity: sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==}
    engines: {node: ^18.18.0 || >=20.0.0}
    peerDependencies:
      eslint: ^8.56.0
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true

  '@typescript-eslint/types@7.16.0':
    resolution: {integrity: sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==}
    engines: {node: ^18.18.0 || >=20.0.0}

  '@typescript-eslint/typescript-estree@7.16.0':
    resolution: {integrity: sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==}
    engines: {node: ^18.18.0 || >=20.0.0}
    peerDependencies:
      typescript: '*'
    peerDependenciesMeta:
      typescript:
        optional: true

  '@typescript-eslint/utils@7.16.0':
    resolution: {integrity: sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==}
    engines: {node: ^18.18.0 || >=20.0.0}
    peerDependencies:
      eslint: ^8.56.0

  '@typescript-eslint/visitor-keys@7.16.0':
    resolution: {integrity: sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==}
    engines: {node: ^18.18.0 || >=20.0.0}

  '@ungap/structured-clone@1.2.0':
    resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}

  '@vitejs/plugin-react-swc@3.7.0':
    resolution: {integrity: sha512-yrknSb3Dci6svCd/qhHqhFPDSw0QtjumcqdKMoNNzmOl5lMXTTiqzjWtG4Qask2HdvvzaNgSunbQGet8/GrKdA==}
    peerDependencies:
      vite: ^4 || ^5

  acorn-jsx@5.3.2:
    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
    peerDependencies:
      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0

  acorn@8.12.1:
    resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==}
    engines: {node: '>=0.4.0'}
    hasBin: true

  ajv@6.12.6:
    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}

  ansi-regex@5.0.1:
    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
    engines: {node: '>=8'}

  ansi-styles@4.3.0:
    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
    engines: {node: '>=8'}

  argparse@2.0.1:
    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}

  array-union@2.1.0:
    resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
    engines: {node: '>=8'}

  balanced-match@1.0.2:
    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}

  brace-expansion@1.1.11:
    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}

  brace-expansion@2.0.1:
    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}

  braces@3.0.3:
    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
    engines: {node: '>=8'}

  callsites@3.1.0:
    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
    engines: {node: '>=6'}

  camelize@1.0.1:
    resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==}

  chalk@4.1.2:
    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
    engines: {node: '>=10'}

  color-convert@2.0.1:
    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
    engines: {node: '>=7.0.0'}

  color-name@1.1.4:
    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}

  concat-map@0.0.1:
    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}

  cross-spawn@7.0.3:
    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
    engines: {node: '>= 8'}

  css-color-keywords@1.0.0:
    resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==}
    engines: {node: '>=4'}

  css-to-react-native@3.2.0:
    resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==}

  csstype@3.1.3:
    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}

  debug@4.3.5:
    resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
    engines: {node: '>=6.0'}
    peerDependencies:
      supports-color: '*'
    peerDependenciesMeta:
      supports-color:
        optional: true

  deep-is@0.1.4:
    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}

  dir-glob@3.0.1:
    resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
    engines: {node: '>=8'}

  doctrine@3.0.0:
    resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
    engines: {node: '>=6.0.0'}

  esbuild@0.21.5:
    resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
    engines: {node: '>=12'}
    hasBin: true

  escape-string-regexp@4.0.0:
    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
    engines: {node: '>=10'}

  eslint-plugin-react-hooks@4.6.2:
    resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==}
    engines: {node: '>=10'}
    peerDependencies:
      eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0

  eslint-plugin-react-refresh@0.4.8:
    resolution: {integrity: sha512-MIKAclwaDFIiYtVBLzDdm16E+Ty4GwhB6wZlCAG1R3Ur+F9Qbo6PRxpA5DK7XtDgm+WlCoAY2WxAwqhmIDHg6Q==}
    peerDependencies:
      eslint: '>=7'

  eslint-scope@7.2.2:
    resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}

  eslint-visitor-keys@3.4.3:
    resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}

  eslint@8.57.0:
    resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==}
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
    hasBin: true

  espree@9.6.1:
    resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}

  esquery@1.6.0:
    resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
    engines: {node: '>=0.10'}

  esrecurse@4.3.0:
    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
    engines: {node: '>=4.0'}

  estraverse@5.3.0:
    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
    engines: {node: '>=4.0'}

  esutils@2.0.3:
    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
    engines: {node: '>=0.10.0'}

  fast-deep-equal@3.1.3:
    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}

  fast-glob@3.3.2:
    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
    engines: {node: '>=8.6.0'}

  fast-json-stable-stringify@2.1.0:
    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}

  fast-levenshtein@2.0.6:
    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}

  fastq@1.17.1:
    resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==}

  file-entry-cache@6.0.1:
    resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
    engines: {node: ^10.12.0 || >=12.0.0}

  fill-range@7.1.1:
    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
    engines: {node: '>=8'}

  find-up@5.0.0:
    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
    engines: {node: '>=10'}

  flat-cache@3.2.0:
    resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
    engines: {node: ^10.12.0 || >=12.0.0}

  flatted@3.3.1:
    resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}

  fs.realpath@1.0.0:
    resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}

  fsevents@2.3.3:
    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
    os: [darwin]

  glob-parent@5.1.2:
    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
    engines: {node: '>= 6'}

  glob-parent@6.0.2:
    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
    engines: {node: '>=10.13.0'}

  glob@7.2.3:
    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
    deprecated: Glob versions prior to v9 are no longer supported

  globals@13.24.0:
    resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
    engines: {node: '>=8'}

  globby@11.1.0:
    resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
    engines: {node: '>=10'}

  graphemer@1.4.0:
    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}

  has-flag@4.0.0:
    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
    engines: {node: '>=8'}

  ignore@5.3.1:
    resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
    engines: {node: '>= 4'}

  import-fresh@3.3.0:
    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
    engines: {node: '>=6'}

  imurmurhash@0.1.4:
    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
    engines: {node: '>=0.8.19'}

  inflight@1.0.6:
    resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
    deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.

  inherits@2.0.4:
    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}

  is-extglob@2.1.1:
    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
    engines: {node: '>=0.10.0'}

  is-glob@4.0.3:
    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
    engines: {node: '>=0.10.0'}

  is-number@7.0.0:
    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
    engines: {node: '>=0.12.0'}

  is-path-inside@3.0.3:
    resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
    engines: {node: '>=8'}

  isexe@2.0.0:
    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}

  js-tokens@4.0.0:
    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}

  js-yaml@4.1.0:
    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
    hasBin: true

  json-buffer@3.0.1:
    resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}

  json-schema-traverse@0.4.1:
    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}

  json-stable-stringify-without-jsonify@1.0.1:
    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}

  keyv@4.5.4:
    resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}

  levn@0.4.1:
    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
    engines: {node: '>= 0.8.0'}

  locate-path@6.0.0:
    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
    engines: {node: '>=10'}

  lodash.merge@4.6.2:
    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}

  loose-envify@1.4.0:
    resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
    hasBin: true

  merge2@1.4.1:
    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
    engines: {node: '>= 8'}

  micromatch@4.0.7:
    resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
    engines: {node: '>=8.6'}

  minimatch@3.1.2:
    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}

  minimatch@9.0.5:
    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
    engines: {node: '>=16 || 14 >=14.17'}

  ms@2.1.2:
    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}

  nanoid@3.3.7:
    resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
    hasBin: true

  natural-compare@1.4.0:
    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}

  once@1.4.0:
    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}

  optionator@0.9.4:
    resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
    engines: {node: '>= 0.8.0'}

  p-limit@3.1.0:
    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
    engines: {node: '>=10'}

  p-locate@5.0.0:
    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
    engines: {node: '>=10'}

  parent-module@1.0.1:
    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
    engines: {node: '>=6'}

  path-exists@4.0.0:
    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
    engines: {node: '>=8'}

  path-is-absolute@1.0.1:
    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
    engines: {node: '>=0.10.0'}

  path-key@3.1.1:
    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
    engines: {node: '>=8'}

  path-type@4.0.0:
    resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
    engines: {node: '>=8'}

  picocolors@1.0.1:
    resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}

  picomatch@2.3.1:
    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
    engines: {node: '>=8.6'}

  postcss-value-parser@4.2.0:
    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}

  postcss@8.4.38:
    resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==}
    engines: {node: ^10 || ^12 || >=14}

  postcss@8.4.39:
    resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==}
    engines: {node: ^10 || ^12 || >=14}

  prelude-ls@1.2.1:
    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
    engines: {node: '>= 0.8.0'}

  punycode@2.3.1:
    resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
    engines: {node: '>=6'}

  queue-microtask@1.2.3:
    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}

  react-dom@18.3.1:
    resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==}
    peerDependencies:
      react: ^18.3.1

  react@18.3.1:
    resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
    engines: {node: '>=0.10.0'}

  resolve-from@4.0.0:
    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
    engines: {node: '>=4'}

  reusify@1.0.4:
    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}

  rimraf@3.0.2:
    resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
    deprecated: Rimraf versions prior to v4 are no longer supported
    hasBin: true

  rollup@4.18.1:
    resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==}
    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
    hasBin: true

  run-parallel@1.2.0:
    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}

  scheduler@0.23.2:
    resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}

  semver@7.6.2:
    resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==}
    engines: {node: '>=10'}
    hasBin: true

  shallowequal@1.1.0:
    resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}

  shebang-command@2.0.0:
    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
    engines: {node: '>=8'}

  shebang-regex@3.0.0:
    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
    engines: {node: '>=8'}

  slash@3.0.0:
    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
    engines: {node: '>=8'}

  source-map-js@1.2.0:
    resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==}
    engines: {node: '>=0.10.0'}

  strip-ansi@6.0.1:
    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
    engines: {node: '>=8'}

  strip-json-comments@3.1.1:
    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
    engines: {node: '>=8'}

  styled-components@6.1.11:
    resolution: {integrity: sha512-Ui0jXPzbp1phYij90h12ksljKGqF8ncGx+pjrNPsSPhbUUjWT2tD1FwGo2LF6USCnbrsIhNngDfodhxbegfEOA==}
    engines: {node: '>= 16'}
    peerDependencies:
      react: '>= 16.8.0'
      react-dom: '>= 16.8.0'

  stylis@4.3.2:
    resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==}

  supports-color@7.2.0:
    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
    engines: {node: '>=8'}

  text-table@0.2.0:
    resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}

  to-regex-range@5.0.1:
    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
    engines: {node: '>=8.0'}

  ts-api-utils@1.3.0:
    resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
    engines: {node: '>=16'}
    peerDependencies:
      typescript: '>=4.2.0'

  tslib@2.6.2:
    resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}

  type-check@0.4.0:
    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
    engines: {node: '>= 0.8.0'}

  type-fest@0.20.2:
    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
    engines: {node: '>=10'}

  typescript@5.5.3:
    resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==}
    engines: {node: '>=14.17'}
    hasBin: true

  uri-js@4.4.1:
    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}

  vite@5.3.3:
    resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==}
    engines: {node: ^18.0.0 || >=20.0.0}
    hasBin: true
    peerDependencies:
      '@types/node': ^18.0.0 || >=20.0.0
      less: '*'
      lightningcss: ^1.21.0
      sass: '*'
      stylus: '*'
      sugarss: '*'
      terser: ^5.4.0
    peerDependenciesMeta:
      '@types/node':
        optional: true
      less:
        optional: true
      lightningcss:
        optional: true
      sass:
        optional: true
      stylus:
        optional: true
      sugarss:
        optional: true
      terser:
        optional: true

  which@2.0.2:
    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
    engines: {node: '>= 8'}
    hasBin: true

  word-wrap@1.2.5:
    resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
    engines: {node: '>=0.10.0'}

  wrappy@1.0.2:
    resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}

  yocto-queue@0.1.0:
    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
    engines: {node: '>=10'}

snapshots:

  '@emotion/is-prop-valid@1.2.2':
    dependencies:
      '@emotion/memoize': 0.8.1

  '@emotion/memoize@0.8.1': {}

  '@emotion/unitless@0.8.1': {}

  '@esbuild/aix-ppc64@0.21.5':
    optional: true

  '@esbuild/android-arm64@0.21.5':
    optional: true

  '@esbuild/android-arm@0.21.5':
    optional: true

  '@esbuild/android-x64@0.21.5':
    optional: true

  '@esbuild/darwin-arm64@0.21.5':
    optional: true

  '@esbuild/darwin-x64@0.21.5':
    optional: true

  '@esbuild/freebsd-arm64@0.21.5':
    optional: true

  '@esbuild/freebsd-x64@0.21.5':
    optional: true

  '@esbuild/linux-arm64@0.21.5':
    optional: true

  '@esbuild/linux-arm@0.21.5':
    optional: true

  '@esbuild/linux-ia32@0.21.5':
    optional: true

  '@esbuild/linux-loong64@0.21.5':
    optional: true

  '@esbuild/linux-mips64el@0.21.5':
    optional: true

  '@esbuild/linux-ppc64@0.21.5':
    optional: true

  '@esbuild/linux-riscv64@0.21.5':
    optional: true

  '@esbuild/linux-s390x@0.21.5':
    optional: true

  '@esbuild/linux-x64@0.21.5':
    optional: true

  '@esbuild/netbsd-x64@0.21.5':
    optional: true

  '@esbuild/openbsd-x64@0.21.5':
    optional: true

  '@esbuild/sunos-x64@0.21.5':
    optional: true

  '@esbuild/win32-arm64@0.21.5':
    optional: true

  '@esbuild/win32-ia32@0.21.5':
    optional: true

  '@esbuild/win32-x64@0.21.5':
    optional: true

  '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)':
    dependencies:
      eslint: 8.57.0
      eslint-visitor-keys: 3.4.3

  '@eslint-community/regexpp@4.11.0': {}

  '@eslint/eslintrc@2.1.4':
    dependencies:
      ajv: 6.12.6
      debug: 4.3.5
      espree: 9.6.1
      globals: 13.24.0
      ignore: 5.3.1
      import-fresh: 3.3.0
      js-yaml: 4.1.0
      minimatch: 3.1.2
      strip-json-comments: 3.1.1
    transitivePeerDependencies:
      - supports-color

  '@eslint/js@8.57.0': {}

  '@humanwhocodes/config-array@0.11.14':
    dependencies:
      '@humanwhocodes/object-schema': 2.0.3
      debug: 4.3.5
      minimatch: 3.1.2
    transitivePeerDependencies:
      - supports-color

  '@humanwhocodes/module-importer@1.0.1': {}

  '@humanwhocodes/object-schema@2.0.3': {}

  '@nodelib/fs.scandir@2.1.5':
    dependencies:
      '@nodelib/fs.stat': 2.0.5
      run-parallel: 1.2.0

  '@nodelib/fs.stat@2.0.5': {}

  '@nodelib/fs.walk@1.2.8':
    dependencies:
      '@nodelib/fs.scandir': 2.1.5
      fastq: 1.17.1

  '@rollup/rollup-android-arm-eabi@4.18.1':
    optional: true

  '@rollup/rollup-android-arm64@4.18.1':
    optional: true

  '@rollup/rollup-darwin-arm64@4.18.1':
    optional: true

  '@rollup/rollup-darwin-x64@4.18.1':
    optional: true

  '@rollup/rollup-linux-arm-gnueabihf@4.18.1':
    optional: true

  '@rollup/rollup-linux-arm-musleabihf@4.18.1':
    optional: true

  '@rollup/rollup-linux-arm64-gnu@4.18.1':
    optional: true

  '@rollup/rollup-linux-arm64-musl@4.18.1':
    optional: true

  '@rollup/rollup-linux-powerpc64le-gnu@4.18.1':
    optional: true

  '@rollup/rollup-linux-riscv64-gnu@4.18.1':
    optional: true

  '@rollup/rollup-linux-s390x-gnu@4.18.1':
    optional: true

  '@rollup/rollup-linux-x64-gnu@4.18.1':
    optional: true

  '@rollup/rollup-linux-x64-musl@4.18.1':
    optional: true

  '@rollup/rollup-win32-arm64-msvc@4.18.1':
    optional: true

  '@rollup/rollup-win32-ia32-msvc@4.18.1':
    optional: true

  '@rollup/rollup-win32-x64-msvc@4.18.1':
    optional: true

  '@swc/core-darwin-arm64@1.6.13':
    optional: true

  '@swc/core-darwin-x64@1.6.13':
    optional: true

  '@swc/core-linux-arm-gnueabihf@1.6.13':
    optional: true

  '@swc/core-linux-arm64-gnu@1.6.13':
    optional: true

  '@swc/core-linux-arm64-musl@1.6.13':
    optional: true

  '@swc/core-linux-x64-gnu@1.6.13':
    optional: true

  '@swc/core-linux-x64-musl@1.6.13':
    optional: true

  '@swc/core-win32-arm64-msvc@1.6.13':
    optional: true

  '@swc/core-win32-ia32-msvc@1.6.13':
    optional: true

  '@swc/core-win32-x64-msvc@1.6.13':
    optional: true

  '@swc/core@1.6.13':
    dependencies:
      '@swc/counter': 0.1.3
      '@swc/types': 0.1.9
    optionalDependencies:
      '@swc/core-darwin-arm64': 1.6.13
      '@swc/core-darwin-x64': 1.6.13
      '@swc/core-linux-arm-gnueabihf': 1.6.13
      '@swc/core-linux-arm64-gnu': 1.6.13
      '@swc/core-linux-arm64-musl': 1.6.13
      '@swc/core-linux-x64-gnu': 1.6.13
      '@swc/core-linux-x64-musl': 1.6.13
      '@swc/core-win32-arm64-msvc': 1.6.13
      '@swc/core-win32-ia32-msvc': 1.6.13
      '@swc/core-win32-x64-msvc': 1.6.13

  '@swc/counter@0.1.3': {}

  '@swc/types@0.1.9':
    dependencies:
      '@swc/counter': 0.1.3

  '@types/estree@1.0.5': {}

  '@types/prop-types@15.7.12': {}

  '@types/react-dom@18.3.0':
    dependencies:
      '@types/react': 18.3.3

  '@types/react@18.3.3':
    dependencies:
      '@types/prop-types': 15.7.12
      csstype: 3.1.3

  '@types/stylis@4.2.5': {}

  '@typescript-eslint/eslint-plugin@7.16.0(@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3))(eslint@8.57.0)(typescript@5.5.3)':
    dependencies:
      '@eslint-community/regexpp': 4.11.0
      '@typescript-eslint/parser': 7.16.0(eslint@8.57.0)(typescript@5.5.3)
      '@typescript-eslint/scope-manager': 7.16.0
      '@typescript-eslint/type-utils': 7.16.0(eslint@8.57.0)(typescript@5.5.3)
      '@typescript-eslint/utils': 7.16.0(eslint@8.57.0)(typescript@5.5.3)
      '@typescript-eslint/visitor-keys': 7.16.0
      eslint: 8.57.0
      graphemer: 1.4.0
      ignore: 5.3.1
      natural-compare: 1.4.0
      ts-api-utils: 1.3.0(typescript@5.5.3)
    optionalDependencies:
      typescript: 5.5.3
    transitivePeerDependencies:
      - supports-color

  '@typescript-eslint/parser@7.16.0(eslint@8.57.0)(typescript@5.5.3)':
    dependencies:
      '@typescript-eslint/scope-manager': 7.16.0
      '@typescript-eslint/types': 7.16.0
      '@typescript-eslint/typescript-estree': 7.16.0(typescript@5.5.3)
      '@typescript-eslint/visitor-keys': 7.16.0
      debug: 4.3.5
      eslint: 8.57.0
    optionalDependencies:
      typescript: 5.5.3
    transitivePeerDependencies:
      - supports-color

  '@typescript-eslint/scope-manager@7.16.0':
    dependencies:
      '@typescript-eslint/types': 7.16.0
      '@typescript-eslint/visitor-keys': 7.16.0

  '@typescript-eslint/type-utils@7.16.0(eslint@8.57.0)(typescript@5.5.3)':
    dependencies:
      '@typescript-eslint/typescript-estree': 7.16.0(typescript@5.5.3)
      '@typescript-eslint/utils': 7.16.0(eslint@8.57.0)(typescript@5.5.3)
      debug: 4.3.5
      eslint: 8.57.0
      ts-api-utils: 1.3.0(typescript@5.5.3)
    optionalDependencies:
      typescript: 5.5.3
    transitivePeerDependencies:
      - supports-color

  '@typescript-eslint/types@7.16.0': {}

  '@typescript-eslint/typescript-estree@7.16.0(typescript@5.5.3)':
    dependencies:
      '@typescript-eslint/types': 7.16.0
      '@typescript-eslint/visitor-keys': 7.16.0
      debug: 4.3.5
      globby: 11.1.0
      is-glob: 4.0.3
      minimatch: 9.0.5
      semver: 7.6.2
      ts-api-utils: 1.3.0(typescript@5.5.3)
    optionalDependencies:
      typescript: 5.5.3
    transitivePeerDependencies:
      - supports-color

  '@typescript-eslint/utils@7.16.0(eslint@8.57.0)(typescript@5.5.3)':
    dependencies:
      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
      '@typescript-eslint/scope-manager': 7.16.0
      '@typescript-eslint/types': 7.16.0
      '@typescript-eslint/typescript-estree': 7.16.0(typescript@5.5.3)
      eslint: 8.57.0
    transitivePeerDependencies:
      - supports-color
      - typescript

  '@typescript-eslint/visitor-keys@7.16.0':
    dependencies:
      '@typescript-eslint/types': 7.16.0
      eslint-visitor-keys: 3.4.3

  '@ungap/structured-clone@1.2.0': {}

  '@vitejs/plugin-react-swc@3.7.0(vite@5.3.3)':
    dependencies:
      '@swc/core': 1.6.13
      vite: 5.3.3
    transitivePeerDependencies:
      - '@swc/helpers'

  acorn-jsx@5.3.2(acorn@8.12.1):
    dependencies:
      acorn: 8.12.1

  acorn@8.12.1: {}

  ajv@6.12.6:
    dependencies:
      fast-deep-equal: 3.1.3
      fast-json-stable-stringify: 2.1.0
      json-schema-traverse: 0.4.1
      uri-js: 4.4.1

  ansi-regex@5.0.1: {}

  ansi-styles@4.3.0:
    dependencies:
      color-convert: 2.0.1

  argparse@2.0.1: {}

  array-union@2.1.0: {}

  balanced-match@1.0.2: {}

  brace-expansion@1.1.11:
    dependencies:
      balanced-match: 1.0.2
      concat-map: 0.0.1

  brace-expansion@2.0.1:
    dependencies:
      balanced-match: 1.0.2

  braces@3.0.3:
    dependencies:
      fill-range: 7.1.1

  callsites@3.1.0: {}

  camelize@1.0.1: {}

  chalk@4.1.2:
    dependencies:
      ansi-styles: 4.3.0
      supports-color: 7.2.0

  color-convert@2.0.1:
    dependencies:
      color-name: 1.1.4

  color-name@1.1.4: {}

  concat-map@0.0.1: {}

  cross-spawn@7.0.3:
    dependencies:
      path-key: 3.1.1
      shebang-command: 2.0.0
      which: 2.0.2

  css-color-keywords@1.0.0: {}

  css-to-react-native@3.2.0:
    dependencies:
      camelize: 1.0.1
      css-color-keywords: 1.0.0
      postcss-value-parser: 4.2.0

  csstype@3.1.3: {}

  debug@4.3.5:
    dependencies:
      ms: 2.1.2

  deep-is@0.1.4: {}

  dir-glob@3.0.1:
    dependencies:
      path-type: 4.0.0

  doctrine@3.0.0:
    dependencies:
      esutils: 2.0.3

  esbuild@0.21.5:
    optionalDependencies:
      '@esbuild/aix-ppc64': 0.21.5
      '@esbuild/android-arm': 0.21.5
      '@esbuild/android-arm64': 0.21.5
      '@esbuild/android-x64': 0.21.5
      '@esbuild/darwin-arm64': 0.21.5
      '@esbuild/darwin-x64': 0.21.5
      '@esbuild/freebsd-arm64': 0.21.5
      '@esbuild/freebsd-x64': 0.21.5
      '@esbuild/linux-arm': 0.21.5
      '@esbuild/linux-arm64': 0.21.5
      '@esbuild/linux-ia32': 0.21.5
      '@esbuild/linux-loong64': 0.21.5
      '@esbuild/linux-mips64el': 0.21.5
      '@esbuild/linux-ppc64': 0.21.5
      '@esbuild/linux-riscv64': 0.21.5
      '@esbuild/linux-s390x': 0.21.5
      '@esbuild/linux-x64': 0.21.5
      '@esbuild/netbsd-x64': 0.21.5
      '@esbuild/openbsd-x64': 0.21.5
      '@esbuild/sunos-x64': 0.21.5
      '@esbuild/win32-arm64': 0.21.5
      '@esbuild/win32-ia32': 0.21.5
      '@esbuild/win32-x64': 0.21.5

  escape-string-regexp@4.0.0: {}

  eslint-plugin-react-hooks@4.6.2(eslint@8.57.0):
    dependencies:
      eslint: 8.57.0

  eslint-plugin-react-refresh@0.4.8(eslint@8.57.0):
    dependencies:
      eslint: 8.57.0

  eslint-scope@7.2.2:
    dependencies:
      esrecurse: 4.3.0
      estraverse: 5.3.0

  eslint-visitor-keys@3.4.3: {}

  eslint@8.57.0:
    dependencies:
      '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
      '@eslint-community/regexpp': 4.11.0
      '@eslint/eslintrc': 2.1.4
      '@eslint/js': 8.57.0
      '@humanwhocodes/config-array': 0.11.14
      '@humanwhocodes/module-importer': 1.0.1
      '@nodelib/fs.walk': 1.2.8
      '@ungap/structured-clone': 1.2.0
      ajv: 6.12.6
      chalk: 4.1.2
      cross-spawn: 7.0.3
      debug: 4.3.5
      doctrine: 3.0.0
      escape-string-regexp: 4.0.0
      eslint-scope: 7.2.2
      eslint-visitor-keys: 3.4.3
      espree: 9.6.1
      esquery: 1.6.0
      esutils: 2.0.3
      fast-deep-equal: 3.1.3
      file-entry-cache: 6.0.1
      find-up: 5.0.0
      glob-parent: 6.0.2
      globals: 13.24.0
      graphemer: 1.4.0
      ignore: 5.3.1
      imurmurhash: 0.1.4
      is-glob: 4.0.3
      is-path-inside: 3.0.3
      js-yaml: 4.1.0
      json-stable-stringify-without-jsonify: 1.0.1
      levn: 0.4.1
      lodash.merge: 4.6.2
      minimatch: 3.1.2
      natural-compare: 1.4.0
      optionator: 0.9.4
      strip-ansi: 6.0.1
      text-table: 0.2.0
    transitivePeerDependencies:
      - supports-color

  espree@9.6.1:
    dependencies:
      acorn: 8.12.1
      acorn-jsx: 5.3.2(acorn@8.12.1)
      eslint-visitor-keys: 3.4.3

  esquery@1.6.0:
    dependencies:
      estraverse: 5.3.0

  esrecurse@4.3.0:
    dependencies:
      estraverse: 5.3.0

  estraverse@5.3.0: {}

  esutils@2.0.3: {}

  fast-deep-equal@3.1.3: {}

  fast-glob@3.3.2:
    dependencies:
      '@nodelib/fs.stat': 2.0.5
      '@nodelib/fs.walk': 1.2.8
      glob-parent: 5.1.2
      merge2: 1.4.1
      micromatch: 4.0.7

  fast-json-stable-stringify@2.1.0: {}

  fast-levenshtein@2.0.6: {}

  fastq@1.17.1:
    dependencies:
      reusify: 1.0.4

  file-entry-cache@6.0.1:
    dependencies:
      flat-cache: 3.2.0

  fill-range@7.1.1:
    dependencies:
      to-regex-range: 5.0.1

  find-up@5.0.0:
    dependencies:
      locate-path: 6.0.0
      path-exists: 4.0.0

  flat-cache@3.2.0:
    dependencies:
      flatted: 3.3.1
      keyv: 4.5.4
      rimraf: 3.0.2

  flatted@3.3.1: {}

  fs.realpath@1.0.0: {}

  fsevents@2.3.3:
    optional: true

  glob-parent@5.1.2:
    dependencies:
      is-glob: 4.0.3

  glob-parent@6.0.2:
    dependencies:
      is-glob: 4.0.3

  glob@7.2.3:
    dependencies:
      fs.realpath: 1.0.0
      inflight: 1.0.6
      inherits: 2.0.4
      minimatch: 3.1.2
      once: 1.4.0
      path-is-absolute: 1.0.1

  globals@13.24.0:
    dependencies:
      type-fest: 0.20.2

  globby@11.1.0:
    dependencies:
      array-union: 2.1.0
      dir-glob: 3.0.1
      fast-glob: 3.3.2
      ignore: 5.3.1
      merge2: 1.4.1
      slash: 3.0.0

  graphemer@1.4.0: {}

  has-flag@4.0.0: {}

  ignore@5.3.1: {}

  import-fresh@3.3.0:
    dependencies:
      parent-module: 1.0.1
      resolve-from: 4.0.0

  imurmurhash@0.1.4: {}

  inflight@1.0.6:
    dependencies:
      once: 1.4.0
      wrappy: 1.0.2

  inherits@2.0.4: {}

  is-extglob@2.1.1: {}

  is-glob@4.0.3:
    dependencies:
      is-extglob: 2.1.1

  is-number@7.0.0: {}

  is-path-inside@3.0.3: {}

  isexe@2.0.0: {}

  js-tokens@4.0.0: {}

  js-yaml@4.1.0:
    dependencies:
      argparse: 2.0.1

  json-buffer@3.0.1: {}

  json-schema-traverse@0.4.1: {}

  json-stable-stringify-without-jsonify@1.0.1: {}

  keyv@4.5.4:
    dependencies:
      json-buffer: 3.0.1

  levn@0.4.1:
    dependencies:
      prelude-ls: 1.2.1
      type-check: 0.4.0

  locate-path@6.0.0:
    dependencies:
      p-locate: 5.0.0

  lodash.merge@4.6.2: {}

  loose-envify@1.4.0:
    dependencies:
      js-tokens: 4.0.0

  merge2@1.4.1: {}

  micromatch@4.0.7:
    dependencies:
      braces: 3.0.3
      picomatch: 2.3.1

  minimatch@3.1.2:
    dependencies:
      brace-expansion: 1.1.11

  minimatch@9.0.5:
    dependencies:
      brace-expansion: 2.0.1

  ms@2.1.2: {}

  nanoid@3.3.7: {}

  natural-compare@1.4.0: {}

  once@1.4.0:
    dependencies:
      wrappy: 1.0.2

  optionator@0.9.4:
    dependencies:
      deep-is: 0.1.4
      fast-levenshtein: 2.0.6
      levn: 0.4.1
      prelude-ls: 1.2.1
      type-check: 0.4.0
      word-wrap: 1.2.5

  p-limit@3.1.0:
    dependencies:
      yocto-queue: 0.1.0

  p-locate@5.0.0:
    dependencies:
      p-limit: 3.1.0

  parent-module@1.0.1:
    dependencies:
      callsites: 3.1.0

  path-exists@4.0.0: {}

  path-is-absolute@1.0.1: {}

  path-key@3.1.1: {}

  path-type@4.0.0: {}

  picocolors@1.0.1: {}

  picomatch@2.3.1: {}

  postcss-value-parser@4.2.0: {}

  postcss@8.4.38:
    dependencies:
      nanoid: 3.3.7
      picocolors: 1.0.1
      source-map-js: 1.2.0

  postcss@8.4.39:
    dependencies:
      nanoid: 3.3.7
      picocolors: 1.0.1
      source-map-js: 1.2.0

  prelude-ls@1.2.1: {}

  punycode@2.3.1: {}

  queue-microtask@1.2.3: {}

  react-dom@18.3.1(react@18.3.1):
    dependencies:
      loose-envify: 1.4.0
      react: 18.3.1
      scheduler: 0.23.2

  react@18.3.1:
    dependencies:
      loose-envify: 1.4.0

  resolve-from@4.0.0: {}

  reusify@1.0.4: {}

  rimraf@3.0.2:
    dependencies:
      glob: 7.2.3

  rollup@4.18.1:
    dependencies:
      '@types/estree': 1.0.5
    optionalDependencies:
      '@rollup/rollup-android-arm-eabi': 4.18.1
      '@rollup/rollup-android-arm64': 4.18.1
      '@rollup/rollup-darwin-arm64': 4.18.1
      '@rollup/rollup-darwin-x64': 4.18.1
      '@rollup/rollup-linux-arm-gnueabihf': 4.18.1
      '@rollup/rollup-linux-arm-musleabihf': 4.18.1
      '@rollup/rollup-linux-arm64-gnu': 4.18.1
      '@rollup/rollup-linux-arm64-musl': 4.18.1
      '@rollup/rollup-linux-powerpc64le-gnu': 4.18.1
      '@rollup/rollup-linux-riscv64-gnu': 4.18.1
      '@rollup/rollup-linux-s390x-gnu': 4.18.1
      '@rollup/rollup-linux-x64-gnu': 4.18.1
      '@rollup/rollup-linux-x64-musl': 4.18.1
      '@rollup/rollup-win32-arm64-msvc': 4.18.1
      '@rollup/rollup-win32-ia32-msvc': 4.18.1
      '@rollup/rollup-win32-x64-msvc': 4.18.1
      fsevents: 2.3.3

  run-parallel@1.2.0:
    dependencies:
      queue-microtask: 1.2.3

  scheduler@0.23.2:
    dependencies:
      loose-envify: 1.4.0

  semver@7.6.2: {}

  shallowequal@1.1.0: {}

  shebang-command@2.0.0:
    dependencies:
      shebang-regex: 3.0.0

  shebang-regex@3.0.0: {}

  slash@3.0.0: {}

  source-map-js@1.2.0: {}

  strip-ansi@6.0.1:
    dependencies:
      ansi-regex: 5.0.1

  strip-json-comments@3.1.1: {}

  styled-components@6.1.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
    dependencies:
      '@emotion/is-prop-valid': 1.2.2
      '@emotion/unitless': 0.8.1
      '@types/stylis': 4.2.5
      css-to-react-native: 3.2.0
      csstype: 3.1.3
      postcss: 8.4.38
      react: 18.3.1
      react-dom: 18.3.1(react@18.3.1)
      shallowequal: 1.1.0
      stylis: 4.3.2
      tslib: 2.6.2

  stylis@4.3.2: {}

  supports-color@7.2.0:
    dependencies:
      has-flag: 4.0.0

  text-table@0.2.0: {}

  to-regex-range@5.0.1:
    dependencies:
      is-number: 7.0.0

  ts-api-utils@1.3.0(typescript@5.5.3):
    dependencies:
      typescript: 5.5.3

  tslib@2.6.2: {}

  type-check@0.4.0:
    dependencies:
      prelude-ls: 1.2.1

  type-fest@0.20.2: {}

  typescript@5.5.3: {}

  uri-js@4.4.1:
    dependencies:
      punycode: 2.3.1

  vite@5.3.3:
    dependencies:
      esbuild: 0.21.5
      postcss: 8.4.39
      rollup: 4.18.1
    optionalDependencies:
      fsevents: 2.3.3

  which@2.0.2:
    dependencies:
      isexe: 2.0.0

  word-wrap@1.2.5: {}

  wrappy@1.0.2: {}

  yocto-queue@0.1.0: {}
0707010000014C000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001D00000000kube-score-1.20.0/web/public0707010000014D000081A4000000000000000000000001680FB1F80000412E000000000000000000000000000000000000002A00000000kube-score-1.20.0/web/public/wasm_exec.js// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

"use strict";

(() => {
	const enosys = () => {
		const err = new Error("not implemented");
		err.code = "ENOSYS";
		return err;
	};

	if (!globalThis.fs) {
		let outputBuf = "";
		globalThis.fs = {
			constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
			writeSync(fd, buf) {
				outputBuf += decoder.decode(buf);
				const nl = outputBuf.lastIndexOf("\n");
				if (nl != -1) {
					console.log(outputBuf.substring(0, nl));
					outputBuf = outputBuf.substring(nl + 1);
				}
				return buf.length;
			},
			write(fd, buf, offset, length, position, callback) {
				if (offset !== 0 || length !== buf.length || position !== null) {
					callback(enosys());
					return;
				}
				const n = this.writeSync(fd, buf);
				callback(null, n);
			},
			chmod(path, mode, callback) { callback(enosys()); },
			chown(path, uid, gid, callback) { callback(enosys()); },
			close(fd, callback) { callback(enosys()); },
			fchmod(fd, mode, callback) { callback(enosys()); },
			fchown(fd, uid, gid, callback) { callback(enosys()); },
			fstat(fd, callback) { callback(enosys()); },
			fsync(fd, callback) { callback(null); },
			ftruncate(fd, length, callback) { callback(enosys()); },
			lchown(path, uid, gid, callback) { callback(enosys()); },
			link(path, link, callback) { callback(enosys()); },
			lstat(path, callback) { callback(enosys()); },
			mkdir(path, perm, callback) { callback(enosys()); },
			open(path, flags, mode, callback) { callback(enosys()); },
			read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
			readdir(path, callback) { callback(enosys()); },
			readlink(path, callback) { callback(enosys()); },
			rename(from, to, callback) { callback(enosys()); },
			rmdir(path, callback) { callback(enosys()); },
			stat(path, callback) { callback(enosys()); },
			symlink(path, link, callback) { callback(enosys()); },
			truncate(path, length, callback) { callback(enosys()); },
			unlink(path, callback) { callback(enosys()); },
			utimes(path, atime, mtime, callback) { callback(enosys()); },
		};
	}

	if (!globalThis.process) {
		globalThis.process = {
			getuid() { return -1; },
			getgid() { return -1; },
			geteuid() { return -1; },
			getegid() { return -1; },
			getgroups() { throw enosys(); },
			pid: -1,
			ppid: -1,
			umask() { throw enosys(); },
			cwd() { throw enosys(); },
			chdir() { throw enosys(); },
		}
	}

	if (!globalThis.crypto) {
		throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)");
	}

	if (!globalThis.performance) {
		throw new Error("globalThis.performance is not available, polyfill required (performance.now only)");
	}

	if (!globalThis.TextEncoder) {
		throw new Error("globalThis.TextEncoder is not available, polyfill required");
	}

	if (!globalThis.TextDecoder) {
		throw new Error("globalThis.TextDecoder is not available, polyfill required");
	}

	const encoder = new TextEncoder("utf-8");
	const decoder = new TextDecoder("utf-8");

	globalThis.Go = class {
		constructor() {
			this.argv = ["js"];
			this.env = {};
			this.exit = (code) => {
				if (code !== 0) {
					console.warn("exit code:", code);
				}
			};
			this._exitPromise = new Promise((resolve) => {
				this._resolveExitPromise = resolve;
			});
			this._pendingEvent = null;
			this._scheduledTimeouts = new Map();
			this._nextCallbackTimeoutID = 1;

			const setInt64 = (addr, v) => {
				this.mem.setUint32(addr + 0, v, true);
				this.mem.setUint32(addr + 4, Math.floor(v / 4294967296), true);
			}

			const setInt32 = (addr, v) => {
				this.mem.setUint32(addr + 0, v, true);
			}

			const getInt64 = (addr) => {
				const low = this.mem.getUint32(addr + 0, true);
				const high = this.mem.getInt32(addr + 4, true);
				return low + high * 4294967296;
			}

			const loadValue = (addr) => {
				const f = this.mem.getFloat64(addr, true);
				if (f === 0) {
					return undefined;
				}
				if (!isNaN(f)) {
					return f;
				}

				const id = this.mem.getUint32(addr, true);
				return this._values[id];
			}

			const storeValue = (addr, v) => {
				const nanHead = 0x7FF80000;

				if (typeof v === "number" && v !== 0) {
					if (isNaN(v)) {
						this.mem.setUint32(addr + 4, nanHead, true);
						this.mem.setUint32(addr, 0, true);
						return;
					}
					this.mem.setFloat64(addr, v, true);
					return;
				}

				if (v === undefined) {
					this.mem.setFloat64(addr, 0, true);
					return;
				}

				let id = this._ids.get(v);
				if (id === undefined) {
					id = this._idPool.pop();
					if (id === undefined) {
						id = this._values.length;
					}
					this._values[id] = v;
					this._goRefCounts[id] = 0;
					this._ids.set(v, id);
				}
				this._goRefCounts[id]++;
				let typeFlag = 0;
				switch (typeof v) {
					case "object":
						if (v !== null) {
							typeFlag = 1;
						}
						break;
					case "string":
						typeFlag = 2;
						break;
					case "symbol":
						typeFlag = 3;
						break;
					case "function":
						typeFlag = 4;
						break;
				}
				this.mem.setUint32(addr + 4, nanHead | typeFlag, true);
				this.mem.setUint32(addr, id, true);
			}

			const loadSlice = (addr) => {
				const array = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				return new Uint8Array(this._inst.exports.mem.buffer, array, len);
			}

			const loadSliceOfValues = (addr) => {
				const array = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				const a = new Array(len);
				for (let i = 0; i < len; i++) {
					a[i] = loadValue(array + i * 8);
				}
				return a;
			}

			const loadString = (addr) => {
				const saddr = getInt64(addr + 0);
				const len = getInt64(addr + 8);
				return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
			}

			const timeOrigin = Date.now() - performance.now();
			this.importObject = {
				_gotest: {
					add: (a, b) => a + b,
				},
				gojs: {
					// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
					// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported
					// function. A goroutine can switch to a new stack if the current stack is too small (see morestack function).
					// This changes the SP, thus we have to update the SP used by the imported function.

					// func wasmExit(code int32)
					"runtime.wasmExit": (sp) => {
						sp >>>= 0;
						const code = this.mem.getInt32(sp + 8, true);
						this.exited = true;
						delete this._inst;
						delete this._values;
						delete this._goRefCounts;
						delete this._ids;
						delete this._idPool;
						this.exit(code);
					},

					// func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
					"runtime.wasmWrite": (sp) => {
						sp >>>= 0;
						const fd = getInt64(sp + 8);
						const p = getInt64(sp + 16);
						const n = this.mem.getInt32(sp + 24, true);
						fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
					},

					// func resetMemoryDataView()
					"runtime.resetMemoryDataView": (sp) => {
						sp >>>= 0;
						this.mem = new DataView(this._inst.exports.mem.buffer);
					},

					// func nanotime1() int64
					"runtime.nanotime1": (sp) => {
						sp >>>= 0;
						setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
					},

					// func walltime() (sec int64, nsec int32)
					"runtime.walltime": (sp) => {
						sp >>>= 0;
						const msec = (new Date).getTime();
						setInt64(sp + 8, msec / 1000);
						this.mem.setInt32(sp + 16, (msec % 1000) * 1000000, true);
					},

					// func scheduleTimeoutEvent(delay int64) int32
					"runtime.scheduleTimeoutEvent": (sp) => {
						sp >>>= 0;
						const id = this._nextCallbackTimeoutID;
						this._nextCallbackTimeoutID++;
						this._scheduledTimeouts.set(id, setTimeout(
							() => {
								this._resume();
								while (this._scheduledTimeouts.has(id)) {
									// for some reason Go failed to register the timeout event, log and try again
									// (temporary workaround for https://github.com/golang/go/issues/28975)
									console.warn("scheduleTimeoutEvent: missed timeout event");
									this._resume();
								}
							},
							getInt64(sp + 8),
						));
						this.mem.setInt32(sp + 16, id, true);
					},

					// func clearTimeoutEvent(id int32)
					"runtime.clearTimeoutEvent": (sp) => {
						sp >>>= 0;
						const id = this.mem.getInt32(sp + 8, true);
						clearTimeout(this._scheduledTimeouts.get(id));
						this._scheduledTimeouts.delete(id);
					},

					// func getRandomData(r []byte)
					"runtime.getRandomData": (sp) => {
						sp >>>= 0;
						crypto.getRandomValues(loadSlice(sp + 8));
					},

					// func finalizeRef(v ref)
					"syscall/js.finalizeRef": (sp) => {
						sp >>>= 0;
						const id = this.mem.getUint32(sp + 8, true);
						this._goRefCounts[id]--;
						if (this._goRefCounts[id] === 0) {
							const v = this._values[id];
							this._values[id] = null;
							this._ids.delete(v);
							this._idPool.push(id);
						}
					},

					// func stringVal(value string) ref
					"syscall/js.stringVal": (sp) => {
						sp >>>= 0;
						storeValue(sp + 24, loadString(sp + 8));
					},

					// func valueGet(v ref, p string) ref
					"syscall/js.valueGet": (sp) => {
						sp >>>= 0;
						const result = Reflect.get(loadValue(sp + 8), loadString(sp + 16));
						sp = this._inst.exports.getsp() >>> 0; // see comment above
						storeValue(sp + 32, result);
					},

					// func valueSet(v ref, p string, x ref)
					"syscall/js.valueSet": (sp) => {
						sp >>>= 0;
						Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
					},

					// func valueDelete(v ref, p string)
					"syscall/js.valueDelete": (sp) => {
						sp >>>= 0;
						Reflect.deleteProperty(loadValue(sp + 8), loadString(sp + 16));
					},

					// func valueIndex(v ref, i int) ref
					"syscall/js.valueIndex": (sp) => {
						sp >>>= 0;
						storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
					},

					// valueSetIndex(v ref, i int, x ref)
					"syscall/js.valueSetIndex": (sp) => {
						sp >>>= 0;
						Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
					},

					// func valueCall(v ref, m string, args []ref) (ref, bool)
					"syscall/js.valueCall": (sp) => {
						sp >>>= 0;
						try {
							const v = loadValue(sp + 8);
							const m = Reflect.get(v, loadString(sp + 16));
							const args = loadSliceOfValues(sp + 32);
							const result = Reflect.apply(m, v, args);
							sp = this._inst.exports.getsp() >>> 0; // see comment above
							storeValue(sp + 56, result);
							this.mem.setUint8(sp + 64, 1);
						} catch (err) {
							sp = this._inst.exports.getsp() >>> 0; // see comment above
							storeValue(sp + 56, err);
							this.mem.setUint8(sp + 64, 0);
						}
					},

					// func valueInvoke(v ref, args []ref) (ref, bool)
					"syscall/js.valueInvoke": (sp) => {
						sp >>>= 0;
						try {
							const v = loadValue(sp + 8);
							const args = loadSliceOfValues(sp + 16);
							const result = Reflect.apply(v, undefined, args);
							sp = this._inst.exports.getsp() >>> 0; // see comment above
							storeValue(sp + 40, result);
							this.mem.setUint8(sp + 48, 1);
						} catch (err) {
							sp = this._inst.exports.getsp() >>> 0; // see comment above
							storeValue(sp + 40, err);
							this.mem.setUint8(sp + 48, 0);
						}
					},

					// func valueNew(v ref, args []ref) (ref, bool)
					"syscall/js.valueNew": (sp) => {
						sp >>>= 0;
						try {
							const v = loadValue(sp + 8);
							const args = loadSliceOfValues(sp + 16);
							const result = Reflect.construct(v, args);
							sp = this._inst.exports.getsp() >>> 0; // see comment above
							storeValue(sp + 40, result);
							this.mem.setUint8(sp + 48, 1);
						} catch (err) {
							sp = this._inst.exports.getsp() >>> 0; // see comment above
							storeValue(sp + 40, err);
							this.mem.setUint8(sp + 48, 0);
						}
					},

					// func valueLength(v ref) int
					"syscall/js.valueLength": (sp) => {
						sp >>>= 0;
						setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
					},

					// valuePrepareString(v ref) (ref, int)
					"syscall/js.valuePrepareString": (sp) => {
						sp >>>= 0;
						const str = encoder.encode(String(loadValue(sp + 8)));
						storeValue(sp + 16, str);
						setInt64(sp + 24, str.length);
					},

					// valueLoadString(v ref, b []byte)
					"syscall/js.valueLoadString": (sp) => {
						sp >>>= 0;
						const str = loadValue(sp + 8);
						loadSlice(sp + 16).set(str);
					},

					// func valueInstanceOf(v ref, t ref) bool
					"syscall/js.valueInstanceOf": (sp) => {
						sp >>>= 0;
						this.mem.setUint8(sp + 24, (loadValue(sp + 8) instanceof loadValue(sp + 16)) ? 1 : 0);
					},

					// func copyBytesToGo(dst []byte, src ref) (int, bool)
					"syscall/js.copyBytesToGo": (sp) => {
						sp >>>= 0;
						const dst = loadSlice(sp + 8);
						const src = loadValue(sp + 32);
						if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
							this.mem.setUint8(sp + 48, 0);
							return;
						}
						const toCopy = src.subarray(0, dst.length);
						dst.set(toCopy);
						setInt64(sp + 40, toCopy.length);
						this.mem.setUint8(sp + 48, 1);
					},

					// func copyBytesToJS(dst ref, src []byte) (int, bool)
					"syscall/js.copyBytesToJS": (sp) => {
						sp >>>= 0;
						const dst = loadValue(sp + 8);
						const src = loadSlice(sp + 16);
						if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
							this.mem.setUint8(sp + 48, 0);
							return;
						}
						const toCopy = src.subarray(0, dst.length);
						dst.set(toCopy);
						setInt64(sp + 40, toCopy.length);
						this.mem.setUint8(sp + 48, 1);
					},

					"debug": (value) => {
						console.log(value);
					},
				}
			};
		}

		async run(instance) {
			if (!(instance instanceof WebAssembly.Instance)) {
				throw new Error("Go.run: WebAssembly.Instance expected");
			}
			this._inst = instance;
			this.mem = new DataView(this._inst.exports.mem.buffer);
			this._values = [ // JS values that Go currently has references to, indexed by reference id
				NaN,
				0,
				null,
				true,
				false,
				globalThis,
				this,
			];
			this._goRefCounts = new Array(this._values.length).fill(Infinity); // number of references that Go has to a JS value, indexed by reference id
			this._ids = new Map([ // mapping from JS values to reference ids
				[0, 1],
				[null, 2],
				[true, 3],
				[false, 4],
				[globalThis, 5],
				[this, 6],
			]);
			this._idPool = [];   // unused ids that have been garbage collected
			this.exited = false; // whether the Go program has exited

			// Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
			let offset = 4096;

			const strPtr = (str) => {
				const ptr = offset;
				const bytes = encoder.encode(str + "\0");
				new Uint8Array(this.mem.buffer, offset, bytes.length).set(bytes);
				offset += bytes.length;
				if (offset % 8 !== 0) {
					offset += 8 - (offset % 8);
				}
				return ptr;
			};

			const argc = this.argv.length;

			const argvPtrs = [];
			this.argv.forEach((arg) => {
				argvPtrs.push(strPtr(arg));
			});
			argvPtrs.push(0);

			const keys = Object.keys(this.env).sort();
			keys.forEach((key) => {
				argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
			});
			argvPtrs.push(0);

			const argv = offset;
			argvPtrs.forEach((ptr) => {
				this.mem.setUint32(offset, ptr, true);
				this.mem.setUint32(offset + 4, 0, true);
				offset += 8;
			});

			// The linker guarantees global data starts from at least wasmMinDataAddr.
			// Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
			const wasmMinDataAddr = 4096 + 8192;
			if (offset >= wasmMinDataAddr) {
				throw new Error("total length of command line and environment variables exceeds limit");
			}

			this._inst.exports.run(argc, argv);
			if (this.exited) {
				this._resolveExitPromise();
			}
			await this._exitPromise;
		}

		_resume() {
			if (this.exited) {
				throw new Error("Go program has already exited");
			}
			this._inst.exports.resume();
			if (this.exited) {
				this._resolveExitPromise();
			}
		}

		_makeFuncWrapper(id) {
			const go = this;
			return function () {
				const event = { id: id, this: this, args: arguments };
				go._pendingEvent = event;
				go._resume();
				return event.result;
			};
		}
	}
})();0707010000014E000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000001A00000000kube-score-1.20.0/web/src0707010000014F000081A4000000000000000000000001680FB1F800000882000000000000000000000000000000000000002200000000kube-score-1.20.0/web/src/App.tsximport styled from 'styled-components'
import kubeScoreLogo from './assets/logo.svg'
import Run from './Run'

function App() {
  return (
    <RootContainer>
      <div style={{ display: 'flex', gap: 16, alignItems: "center", justifyItems: "center", justifyContent: "space-between", flex: 1, flexShrink: 0, flexWrap: "wrap", width: "100%" }}>
        <div style={{ display: 'flex', gap: 8, alignItems: "center", flex: 1, flexShrink: 0, }}>
          <img src={kubeScoreLogo} style={{ height: 60 }} alt="kube-score logo" />
          <h1 style={{ fontSize: 32, textWrap: "nowrap" }}>kube-score</h1>
        </div>

        <div style={{ display: 'flex', gap: 16, alignItems: "center", fontSize: 16, flex: 1, flexShrink: 0 }}>
          <div style={{ flex: 1 }}></div>
          <a href="https://github.com/zegl/kube-score">GitHub</a>
          <a href="https://github.com/zegl/kube-score">README</a>
          <a href="https://github.com/zegl/kube-score/blob/master/README_CHECKS.md">Docs</a>
        </div>

      </div>

      <div style={{ fontSize: 14 }}>
        <p>Kubernetes object analysis with recommendations for improved reliability and security.</p>
        <p><code>kube-score</code> is a tool that does static code analysis of your Kubernetes object definitions. The
          output is
          a list of
          recommendations of what you can improve to make your application more secure and resilient.</p>
        <p><code>kube-score</code> is <a href="https://github.com/zegl/kube-score">open-source and available under the
          MIT-license</a>. For more information about how to use kube-score, see <a
            href="https://github.com/zegl/kube-score">zegl/kube-score</a> on GitHub.
          Use this website to easily test kube-score, just paste your object definition YAML or JSON in the box below.
        </p>

        <p>
          This tool is running 100% in your browser, no data is sent to any server!
        </p>
      </div>

      <Run />
    </RootContainer>
  )
}


const RootContainer = styled.div`
  margin: 0 auto;
  padding: 0.5;
  text-align: left;

  @media (min-width: 1024px) {
    padding: 2rem;
  }
`

export default App
07070100000150000081A4000000000000000000000001680FB1F80000102A000000000000000000000000000000000000002200000000kube-score-1.20.0/web/src/Run.tsximport { useEffect, useState } from "react";
import './assets/term.css'
import styled from "styled-components";

type Format = "human" | "ci" | "json" | "sarif" | "junit"

const FormatNames: Record<Format, string> = {
    "human": "CLI",
    "ci": "CI",
    "json": "JSON",
    "sarif": "SARIF",
    "junit": "JUnit",
}

// Types for the WebAssembly module
declare global {
    interface Window {
        handleScore: (input: string, type: Format) => string;
    }
}


function Run() {
    const [input, setInput] = useState(`apiVersion: apps/v1
kind: Deployment
metadata:
  name: statefulset-test-1
spec:
  template:
    metadata:
      labels:
        app: foo
    spec:
      containers:
      - name: foobar
        image: foo:bar
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
  name: app-budget
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: not-foo`)


    const [wasmLoading, setWasmLoading] = useState(false)
    const [result, setResult] = useState("")
    const [format, setFormat] = useState<Format>("human")

    const scoreWhenReady = () => {
        if (typeof window.handleScore === "undefined") {
            setWasmLoading(true)
            setTimeout(scoreWhenReady, 100);
            return;
        }

        score();
    }

    const score = () => {
        setWasmLoading(false)
        setResult("Running...")
        setResult(window.handleScore(input, format));
    }

    useEffect(() => {
        scoreWhenReady()
    }, [input, format])

    const output = wasmLoading ? "LOADING_WASM" :
        !input ? "NO_INPUT" :
            result ? "RESULT" : "RUNNING" as const

    return (
        <div>
            <div style={{color: "#4e4e4e", fontSize: 12}}>Output format</div>
                <div style={{ display: "flex", gap: 2, alignItems: "center" }}>
                    {Object.entries(FormatNames).map(([f, name]) => <OutputFormatButton $selected={format == f} key={f} onClick={() => setFormat(f as Format)}>{name}</OutputFormatButton>)}
                </div>


        <InputOutputContainer>
            <InputTextarea value={input} onChange={(e) => setInput(e.target.value)}></InputTextarea>

            <div style={{ flex: 1, flexShrink: 0, overflow: "hidden", display: "flex", gap: 4, flexDirection: "column" }}>
            
                

                <TermContainer>
                    {output === "LOADING_WASM" && <div>Loading WebAssembly...</div>}
                    {output === "NO_INPUT" && <div>Paste your Kubernetes YAML in the textarea to get started</div>}
                    {output === "RESULT" && <>
                        {format === "human" ? <div dangerouslySetInnerHTML={{ __html: result }}></div> : <div>{result}</div>}
                    </>}
                    {output === "RUNNING" && <div>Running...</div>}
                </TermContainer></div>
        </InputOutputContainer>
        </div>
    );
}

const InputOutputContainer = styled.div`
    display: flex;
    gap: 8px;
    margin-top: 16px;

    flex-direction: column;

    @media (min-width: 1024px) {
        flex-direction: row;
  }
`

const OutputFormatButton = styled.button<{ $selected: boolean; }>`
    background: ${props => props.$selected ? "#c172a9" : "#474747"};
    color: ${props => props.$selected ? "white" : "white"};
    border-radius: 4px;
    padding: 6px 10px;
    border: 0;
    outline-offset: 2px;
    cursor: pointer;
    font-size: 14px;
`

const TermContainer = styled.div`
    background: #171717;
    border-radius: 5px;
    color: white;
    word-break: break-word;
    overflow-wrap: break-word;
    font-family: "SFMono-Regular", Monaco, Menlo, Consolas, "Liberation Mono", Courier, monospace;
    font-size: 14px;
    line-height: 20px;
    padding: 14px 18px;
    white-space: pre-wrap;
    flex: 1;
    flex-shrink: 0;
`

const InputTextarea = styled.textarea`
    border-radius: 5px;
    font-family: "SFMono-Regular", Monaco, Menlo, Consolas, "Liberation Mono", Courier, monospace;
    font-size: 14px;
    line-height: 20px;
    padding: 14px 18px;
    flex: 1;
    flex-shrink: 0;
    min-height: 200px
`

export default Run;07070100000151000041ED000000000000000000000002680FB1F800000000000000000000000000000000000000000000002100000000kube-score-1.20.0/web/src/assets07070100000152000081A4000000000000000000000001680FB1F800000456000000000000000000000000000000000000002A00000000kube-score-1.20.0/web/src/assets/logo.svg<?xml version="1.0" encoding="UTF-8"?>
<svg viewBox="0 195 900 300" xmlns="http://www.w3.org/2000/svg">
    <path d="m799.56 319.34l0.44 57.6-199.47 115.27 0.88995-58.45 198.14-114.42z" fill="#6c97b4"/>
    <path d="m699.63 319.29l-49.65 28.56-0.01001-0.01001-50.42-28.81-0.73999-57.94 100.82 58.2z" fill="#99d6ff"/>
    <path d="m601.42 433.76l-0.88995 58.45-150.29-86.8-50.27-29.03-50.14-28.95-50.06-28.92v-57.97l301.65 173.22z" fill="#99d6ff"/>
    <path d="m598.81 261.09l0.73999 57.94-49.84 28.78-50.11-28.94 99.21-57.78z" fill="#6c97b4"/>
    <path d="m450.24 405.41l-0.019989 0.029999-149.56 86.17-0.089996-57.85 99.4-57.38 50.27 29.03z" fill="#6c97b4"/>
    <path d="m300.57 433.76l0.089996 57.85-200.66-115.82v-57.82l200.57 115.79z" fill="#99d6ff"/>
    <path d="m299.77 260.54v57.97l-49.85 28.7-50.27-28.98 100.12-57.69z" fill="#6c97b4"/>
    <path d="M499.6,318.87l50.11,28.94,50.08,28.91L650,347.85l49.65-28.56-100.82-58.2Zm-50.15-29,149.43-86.32L799.56,319.34,601.42,433.77,299.77,260.54,199.65,318.23l50.27,29,50.19,28.93,49.73-28.72,50.13,29-99.4,57.39L100,318,299,203Z" fill="#ffd4f2"/>
</svg>
07070100000153000081A4000000000000000000000001680FB1F80000292A000000000000000000000000000000000000002A00000000kube-score-1.20.0/web/src/assets/term.css.term-fg1 {}

/* don't bold beccause it looks weird */
.term-fg2 {
    color: #838887;
}

/* faint (decreased intensity) - same as gray really */
.term-fg3 {
    font-style: italic;
}

/* italic */
.term-fg4 {
    text-decoration: underline;
}

/* underline */
.term-fg5 {
    animation: blink-animation 1s steps(3, start) infinite;
}

/* blink */
.term-fg9 {
    text-decoration: line-through;
}

/* crossed-out */

.term-fg30 {
    color: #666666;
}

/* black (but we can't use black, so a diff color) */
.term-fg31 {
    color: #ff7070;
}

/* red */
.term-fg32 {
    color: #b0f986;
}

/* green */
.term-fg33 {
    color: #c6c502;
}

/* yellow */
.term-fg34 {
    color: #8db7e0;
}

/* blue */
.term-fg35 {
    color: #f271fb;
}

/* magenta */
.term-fg36 {
    color: #6bf7ff;
}

/* cyan */

/* high intense colors */
.term-fgi1 {
    color: #5ef765;
}

.term-fgi90 {
    color: #838887;
}

/* grey */
.term-fgi91 {
    color: #ff3333;
}

/* red */
.term-fgi92 {
    color: #00FF00;
}

/* green */
.term-fgi93 {
    color: #fffc67;
}

/* yellow */
.term-fgi94 {
    color: #6871ff;
}

/* blue */
.term-fgi95 {
    color: #ff76ff;
}

/* magenta */
.term-fgi96 {
    color: #60fcff;
}

/* cyan */

/* background colors */
.term-bg40 {
    background: #676767;
}

/* grey */
.term-bg41 {
    background: #ff4343;
}

/* red */
.term-bg42 {
    background: #99ff5f;
}

/* green */

/* custom foreground/background combos for readability */
.term-fg31.term-bg40 {
    color: #F8A39F;
}

/* xterm colors */
.term-fgx16 {
    color: #000000;
}

.term-fgx17 {
    color: #00005f;
}

.term-fgx18 {
    color: #000087;
}

.term-fgx19 {
    color: #0000af;
}

.term-fgx20 {
    color: #0000d7;
}

.term-fgx21 {
    color: #0000ff;
}

.term-fgx22 {
    color: #005f00;
}

.term-fgx23 {
    color: #005f5f;
}

.term-fgx24 {
    color: #005f87;
}

.term-fgx25 {
    color: #005faf;
}

.term-fgx26 {
    color: #005fd7;
}

.term-fgx27 {
    color: #005fff;
}

.term-fgx28 {
    color: #008700;
}

.term-fgx29 {
    color: #00875f;
}

.term-fgx30 {
    color: #008787;
}

.term-fgx31 {
    color: #0087af;
}

.term-fgx32 {
    color: #0087d7;
}

.term-fgx33 {
    color: #0087ff;
}

.term-fgx34 {
    color: #00af00;
}

.term-fgx35 {
    color: #00af5f;
}

.term-fgx36 {
    color: #00af87;
}

.term-fgx37 {
    color: #00afaf;
}

.term-fgx38 {
    color: #00afd7;
}

.term-fgx39 {
    color: #00afff;
}

.term-fgx40 {
    color: #00d700;
}

.term-fgx41 {
    color: #00d75f;
}

.term-fgx42 {
    color: #00d787;
}

.term-fgx43 {
    color: #00d7af;
}

.term-fgx44 {
    color: #00d7d7;
}

.term-fgx45 {
    color: #00d7ff;
}

.term-fgx46 {
    color: #00ff00;
}

.term-fgx47 {
    color: #00ff5f;
}

.term-fgx48 {
    color: #00ff87;
}

.term-fgx49 {
    color: #00ffaf;
}

.term-fgx50 {
    color: #00ffd7;
}

.term-fgx51 {
    color: #00ffff;
}

.term-fgx52 {
    color: #5f0000;
}

.term-fgx53 {
    color: #5f005f;
}

.term-fgx54 {
    color: #5f0087;
}

.term-fgx55 {
    color: #5f00af;
}

.term-fgx56 {
    color: #5f00d7;
}

.term-fgx57 {
    color: #5f00ff;
}

.term-fgx58 {
    color: #5f5f00;
}

.term-fgx59 {
    color: #5f5f5f;
}

.term-fgx60 {
    color: #5f5f87;
}

.term-fgx61 {
    color: #5f5faf;
}

.term-fgx62 {
    color: #5f5fd7;
}

.term-fgx63 {
    color: #5f5fff;
}

.term-fgx64 {
    color: #5f8700;
}

.term-fgx65 {
    color: #5f875f;
}

.term-fgx66 {
    color: #5f8787;
}

.term-fgx67 {
    color: #5f87af;
}

.term-fgx68 {
    color: #5f87d7;
}

.term-fgx69 {
    color: #5f87ff;
}

.term-fgx70 {
    color: #5faf00;
}

.term-fgx71 {
    color: #5faf5f;
}

.term-fgx72 {
    color: #5faf87;
}

.term-fgx73 {
    color: #5fafaf;
}

.term-fgx74 {
    color: #5fafd7;
}

.term-fgx75 {
    color: #5fafff;
}

.term-fgx76 {
    color: #5fd700;
}

.term-fgx77 {
    color: #5fd75f;
}

.term-fgx78 {
    color: #5fd787;
}

.term-fgx79 {
    color: #5fd7af;
}

.term-fgx80 {
    color: #5fd7d7;
}

.term-fgx81 {
    color: #5fd7ff;
}

.term-fgx82 {
    color: #5fff00;
}

.term-fgx83 {
    color: #5fff5f;
}

.term-fgx84 {
    color: #5fff87;
}

.term-fgx85 {
    color: #5fffaf;
}

.term-fgx86 {
    color: #5fffd7;
}

.term-fgx87 {
    color: #5fffff;
}

.term-fgx88 {
    color: #870000;
}

.term-fgx89 {
    color: #87005f;
}

.term-fgx90 {
    color: #870087;
}

.term-fgx91 {
    color: #8700af;
}

.term-fgx92 {
    color: #8700d7;
}

.term-fgx93 {
    color: #8700ff;
}

.term-fgx94 {
    color: #875f00;
}

.term-fgx95 {
    color: #875f5f;
}

.term-fgx96 {
    color: #875f87;
}

.term-fgx97 {
    color: #875faf;
}

.term-fgx98 {
    color: #875fd7;
}

.term-fgx99 {
    color: #875fff;
}

.term-fgx100 {
    color: #878700;
}

.term-fgx101 {
    color: #87875f;
}

.term-fgx102 {
    color: #878787;
}

.term-fgx103 {
    color: #8787af;
}

.term-fgx104 {
    color: #8787d7;
}

.term-fgx105 {
    color: #8787ff;
}

.term-fgx106 {
    color: #87af00;
}

.term-fgx107 {
    color: #87af5f;
}

.term-fgx108 {
    color: #87af87;
}

.term-fgx109 {
    color: #87afaf;
}

.term-fgx110 {
    color: #87afd7;
}

.term-fgx111 {
    color: #87afff;
}

.term-fgx112 {
    color: #87d700;
}

.term-fgx113 {
    color: #87d75f;
}

.term-fgx114 {
    color: #87d787;
}

.term-fgx115 {
    color: #87d7af;
}

.term-fgx116 {
    color: #87d7d7;
}

.term-fgx117 {
    color: #87d7ff;
}

.term-fgx118 {
    color: #87ff00;
}

.term-fgx119 {
    color: #87ff5f;
}

.term-fgx120 {
    color: #87ff87;
}

.term-fgx121 {
    color: #87ffaf;
}

.term-fgx122 {
    color: #87ffd7;
}

.term-fgx123 {
    color: #87ffff;
}

.term-fgx124 {
    color: #af0000;
}

.term-fgx125 {
    color: #af005f;
}

.term-fgx126 {
    color: #af0087;
}

.term-fgx127 {
    color: #af00af;
}

.term-fgx128 {
    color: #af00d7;
}

.term-fgx129 {
    color: #af00ff;
}

.term-fgx130 {
    color: #af5f00;
}

.term-fgx131 {
    color: #af5f5f;
}

.term-fgx132 {
    color: #af5f87;
}

.term-fgx133 {
    color: #af5faf;
}

.term-fgx134 {
    color: #af5fd7;
}

.term-fgx135 {
    color: #af5fff;
}

.term-fgx136 {
    color: #af8700;
}

.term-fgx137 {
    color: #af875f;
}

.term-fgx138 {
    color: #af8787;
}

.term-fgx139 {
    color: #af87af;
}

.term-fgx140 {
    color: #af87d7;
}

.term-fgx141 {
    color: #af87ff;
}

.term-fgx142 {
    color: #afaf00;
}

.term-fgx143 {
    color: #afaf5f;
}

.term-fgx144 {
    color: #afaf87;
}

.term-fgx145 {
    color: #afafaf;
}

.term-fgx146 {
    color: #afafd7;
}

.term-fgx147 {
    color: #afafff;
}

.term-fgx148 {
    color: #afd700;
}

.term-fgx149 {
    color: #afd75f;
}

.term-fgx150 {
    color: #afd787;
}

.term-fgx151 {
    color: #afd7af;
}

.term-fgx152 {
    color: #afd7d7;
}

.term-fgx153 {
    color: #afd7ff;
}

.term-fgx154 {
    color: #afff00;
}

.term-fgx155 {
    color: #afff5f;
}

.term-fgx156 {
    color: #afff87;
}

.term-fgx157 {
    color: #afffaf;
}

.term-fgx158 {
    color: #afffd7;
}

.term-fgx159 {
    color: #afffff;
}

.term-fgx160 {
    color: #d70000;
}

.term-fgx161 {
    color: #d7005f;
}

.term-fgx162 {
    color: #d70087;
}

.term-fgx163 {
    color: #d700af;
}

.term-fgx164 {
    color: #d700d7;
}

.term-fgx165 {
    color: #d700ff;
}

.term-fgx166 {
    color: #d75f00;
}

.term-fgx167 {
    color: #d75f5f;
}

.term-fgx168 {
    color: #d75f87;
}

.term-fgx169 {
    color: #d75faf;
}

.term-fgx170 {
    color: #d75fd7;
}

.term-fgx171 {
    color: #d75fff;
}

.term-fgx172 {
    color: #d78700;
}

.term-fgx173 {
    color: #d7875f;
}

.term-fgx174 {
    color: #d78787;
}

.term-fgx175 {
    color: #d787af;
}

.term-fgx176 {
    color: #d787d7;
}

.term-fgx177 {
    color: #d787ff;
}

.term-fgx178 {
    color: #d7af00;
}

.term-fgx179 {
    color: #d7af5f;
}

.term-fgx180 {
    color: #d7af87;
}

.term-fgx181 {
    color: #d7afaf;
}

.term-fgx182 {
    color: #d7afd7;
}

.term-fgx183 {
    color: #d7afff;
}

.term-fgx184 {
    color: #d7d700;
}

.term-fgx185 {
    color: #d7d75f;
}

.term-fgx186 {
    color: #d7d787;
}

.term-fgx187 {
    color: #d7d7af;
}

.term-fgx188 {
    color: #d7d7d7;
}

.term-fgx189 {
    color: #d7d7ff;
}

.term-fgx190 {
    color: #d7ff00;
}

.term-fgx191 {
    color: #d7ff5f;
}

.term-fgx192 {
    color: #d7ff87;
}

.term-fgx193 {
    color: #d7ffaf;
}

.term-fgx194 {
    color: #d7ffd7;
}

.term-fgx195 {
    color: #d7ffff;
}

.term-fgx196 {
    color: #ff0000;
}

.term-fgx197 {
    color: #ff005f;
}

.term-fgx198 {
    color: #ff0087;
}

.term-fgx199 {
    color: #ff00af;
}

.term-fgx200 {
    color: #ff00d7;
}

.term-fgx201 {
    color: #ff00ff;
}

.term-fgx202 {
    color: #ff5f00;
}

.term-fgx203 {
    color: #ff5f5f;
}

.term-fgx204 {
    color: #ff5f87;
}

.term-fgx205 {
    color: #ff5faf;
}

.term-fgx206 {
    color: #ff5fd7;
}

.term-fgx207 {
    color: #ff5fff;
}

.term-fgx208 {
    color: #ff8700;
}

.term-fgx209 {
    color: #ff875f;
}

.term-fgx210 {
    color: #ff8787;
}

.term-fgx211 {
    color: #ff87af;
}

.term-fgx212 {
    color: #ff87d7;
}

.term-fgx213 {
    color: #ff87ff;
}

.term-fgx214 {
    color: #ffaf00;
}

.term-fgx215 {
    color: #ffaf5f;
}

.term-fgx216 {
    color: #ffaf87;
}

.term-fgx217 {
    color: #ffafaf;
}

.term-fgx218 {
    color: #ffafd7;
}

.term-fgx219 {
    color: #ffafff;
}

.term-fgx220 {
    color: #ffd700;
}

.term-fgx221 {
    color: #ffd75f;
}

.term-fgx222 {
    color: #ffd787;
}

.term-fgx223 {
    color: #ffd7af;
}

.term-fgx224 {
    color: #ffd7d7;
}

.term-fgx225 {
    color: #ffd7ff;
}

.term-fgx226 {
    color: #ffff00;
}

.term-fgx227 {
    color: #ffff5f;
}

.term-fgx228 {
    color: #ffff87;
}

.term-fgx229 {
    color: #ffffaf;
}

.term-fgx230 {
    color: #ffffd7;
}

.term-fgx231 {
    color: #ffffff;
}

.term-fgx232 {
    color: #080808;
}

.term-fgx233 {
    color: #121212;
}

.term-fgx234 {
    color: #1c1c1c;
}

.term-fgx235 {
    color: #262626;
}

.term-fgx236 {
    color: #303030;
}

.term-fgx237 {
    color: #3a3a3a;
}

.term-fgx238 {
    color: #444444;
}

.term-fgx239 {
    color: #4e4e4e;
}

.term-fgx240 {
    color: #585858;
}

.term-fgx241 {
    color: #626262;
}

.term-fgx242 {
    color: #6c6c6c;
}

.term-fgx243 {
    color: #767676;
}

.term-fgx244 {
    color: #808080;
}

.term-fgx245 {
    color: #8a8a8a;
}

.term-fgx246 {
    color: #949494;
}

.term-fgx247 {
    color: #9e9e9e;
}

.term-fgx248 {
    color: #a8a8a8;
}

.term-fgx249 {
    color: #b2b2b2;
}

.term-fgx250 {
    color: #bcbcbc;
}

.term-fgx251 {
    color: #c6c6c6;
}

.term-fgx252 {
    color: #d0d0d0;
}

.term-fgx253 {
    color: #dadada;
}

.term-fgx254 {
    color: #e4e4e4;
}

.term-fgx255 {
    color: #eeeeee;
}
07070100000154000081A4000000000000000000000001680FB1F80000016B000000000000000000000000000000000000002400000000kube-score-1.20.0/web/src/index.css:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  font-weight: 400;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}
a:hover {
  color: #535bf2;
}07070100000155000081A4000000000000000000000001680FB1F8000000EC000000000000000000000000000000000000002300000000kube-score-1.20.0/web/src/main.tsximport React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import './index.css'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)
07070100000156000081A4000000000000000000000001680FB1F800000026000000000000000000000000000000000000002800000000kube-score-1.20.0/web/src/vite-env.d.ts/// <reference types="vite/client" />
07070100000157000081A4000000000000000000000001680FB1F8000002A5000000000000000000000000000000000000002800000000kube-score-1.20.0/web/tsconfig.app.json{
  "compilerOptions": {
    "composite": true,
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "moduleDetection": "force",
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"]
}
07070100000158000081A4000000000000000000000001680FB1F80000008B000000000000000000000000000000000000002400000000kube-score-1.20.0/web/tsconfig.json{
  "files": [],
  "references": [
    {
      "path": "./tsconfig.app.json"
    },
    {
      "path": "./tsconfig.node.json"
    }
  ]
}
07070100000159000081A4000000000000000000000001680FB1F800000145000000000000000000000000000000000000002900000000kube-score-1.20.0/web/tsconfig.node.json{
  "compilerOptions": {
    "composite": true,
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "noEmit": true
  },
  "include": ["vite.config.ts"]
}
0707010000015A000081A4000000000000000000000001680FB1F8000000A7000000000000000000000000000000000000002500000000kube-score-1.20.0/web/vite.config.tsimport { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
})
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1218 blocks
openSUSE Build Service is sponsored by