mirror of
https://github.com/actions-runner-controller/actions-runner-controller.git
synced 2026-09-30 05:42:14 +02:00
Reject unsafe chart metadata integers (#4679)
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
{{/*
|
||||
Render a single label or annotation value as a string.
|
||||
Values from a values file arrive as float64, so "%v" would turn large integers into
|
||||
scientific notation (12345678901234 -> 1.2345678901234e+13) and silently write a value the
|
||||
user never asked for. Integral floats are therefore formatted without an exponent.
|
||||
Values from a values file arrive as float64. Integral values in the IEEE 754 safe integer
|
||||
range are formatted without an exponent. Unsafe integral values are rejected by assert-scalar
|
||||
before reaching this helper, because their original value may already have been rounded.
|
||||
*/}}
|
||||
{{- define "metadata-value" -}}
|
||||
{{- if eq . nil -}}
|
||||
@@ -50,6 +50,8 @@ Expects a dict with "value", "key", "kind" and "path".
|
||||
{{- $value := .value -}}
|
||||
{{- if or (kindIs "map" $value) (kindIs "slice" $value) (kindIs "invalid" $value) -}}
|
||||
{{- fail (printf "%s: invalid value for %s %q: must be a scalar, got %s. Quote the value if it is meant to be a string" .path .kind .key (kindOf $value)) -}}
|
||||
{{- else if and (or (kindIs "int" $value) (kindIs "int64" $value) (and (kindIs "float64" $value) (eq $value (floor $value)))) (or (ge (float64 $value) 9007199254740992.0) (le (float64 $value) -9007199254740992.0)) -}}
|
||||
{{- fail (printf "%s: invalid value for %s %q: unquoted integers outside the IEEE 754 safe range must be quoted to preserve their exact value" .path .kind .key) -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
@@ -72,6 +74,11 @@ Expects a dict with "key", "kind" (label|annotation) and "path" (the values path
|
||||
{{- if gt (len $prefix) 253 -}}
|
||||
{{- fail (printf "%s: invalid %s key %q: the prefix %q must be a DNS subdomain of no more than 253 characters" $path $kind $key $prefix) -}}
|
||||
{{- end -}}
|
||||
{{- range $segment := splitList "." $prefix -}}
|
||||
{{- if gt (len $segment) 63 -}}
|
||||
{{- fail (printf "%s: invalid %s key %q: the prefix segment %q must be no more than 63 characters" $path $kind $key $segment) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not (regexMatch "^[a-z0-9]([-a-z0-9]*[a-z0-9])?([.][a-z0-9]([-a-z0-9]*[a-z0-9])?)*$" $prefix) -}}
|
||||
{{- fail (printf "%s: invalid %s key %q: the prefix %q must be a DNS subdomain, so it must consist of dot-separated segments of lowercase alphanumeric characters or '-', each starting and ending with an alphanumeric character" $path $kind $key $prefix) -}}
|
||||
{{- end -}}
|
||||
@@ -318,5 +325,3 @@ Behavior:
|
||||
path: {{ $key | quote }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
|
||||
+114
-4
@@ -92,6 +92,7 @@ tests:
|
||||
sync-wave: 1
|
||||
annotations:
|
||||
enabled: true
|
||||
quoted-unsafe-integer: "9007199254740993"
|
||||
runner:
|
||||
pod:
|
||||
metadata:
|
||||
@@ -110,6 +111,95 @@ tests:
|
||||
- equal:
|
||||
path: spec.ephemeralRunnerMetadata.annotations["enabled"]
|
||||
value: "true"
|
||||
- equal:
|
||||
path: spec.ephemeralRunnerMetadata.annotations["quoted-unsafe-integer"]
|
||||
value: "9007199254740993"
|
||||
|
||||
- it: should render the inclusive safe integer boundaries without an exponent
|
||||
set:
|
||||
scaleset.name: "test"
|
||||
auth.url: "https://github.com/org"
|
||||
auth.githubToken: "gh_token12345"
|
||||
controllerServiceAccount.name: "arc"
|
||||
controllerServiceAccount.namespace: "arc-system"
|
||||
resource:
|
||||
all:
|
||||
metadata:
|
||||
labels:
|
||||
maximum-safe-integer: 9007199254740991
|
||||
annotations:
|
||||
maximum-safe-integer: 9007199254740991
|
||||
minimum-safe-integer: -9007199254740991
|
||||
release:
|
||||
name: "test-name"
|
||||
namespace: "test-namespace"
|
||||
asserts:
|
||||
- equal:
|
||||
path: metadata.labels["maximum-safe-integer"]
|
||||
value: "9007199254740991"
|
||||
- equal:
|
||||
path: metadata.annotations["maximum-safe-integer"]
|
||||
value: "9007199254740991"
|
||||
- equal:
|
||||
path: metadata.annotations["minimum-safe-integer"]
|
||||
value: "-9007199254740991"
|
||||
|
||||
- it: should fail at the first unsafe positive integer
|
||||
set:
|
||||
scaleset.name: "test"
|
||||
auth.url: "https://github.com/org"
|
||||
auth.githubToken: "gh_token12345"
|
||||
controllerServiceAccount.name: "arc"
|
||||
controllerServiceAccount.namespace: "arc-system"
|
||||
resource:
|
||||
all:
|
||||
metadata:
|
||||
annotations:
|
||||
unsafe-integer: 9007199254740992
|
||||
release:
|
||||
name: "test-name"
|
||||
namespace: "test-namespace"
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: '.Values.resource.all.metadata.annotations: invalid value for annotation "unsafe-integer": unquoted integers outside the IEEE 754 safe range must be quoted to preserve their exact value'
|
||||
|
||||
- it: should fail at the first unsafe negative integer
|
||||
set:
|
||||
scaleset.name: "test"
|
||||
auth.url: "https://github.com/org"
|
||||
auth.githubToken: "gh_token12345"
|
||||
controllerServiceAccount.name: "arc"
|
||||
controllerServiceAccount.namespace: "arc-system"
|
||||
resource:
|
||||
all:
|
||||
metadata:
|
||||
annotations:
|
||||
unsafe-integer: -9007199254740992
|
||||
release:
|
||||
name: "test-name"
|
||||
namespace: "test-namespace"
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: '.Values.resource.all.metadata.annotations: invalid value for annotation "unsafe-integer": unquoted integers outside the IEEE 754 safe range must be quoted to preserve their exact value'
|
||||
|
||||
- it: should fail when an unquoted metadata integer is outside the IEEE 754 safe range
|
||||
set:
|
||||
scaleset.name: "test"
|
||||
auth.url: "https://github.com/org"
|
||||
auth.githubToken: "gh_token12345"
|
||||
controllerServiceAccount.name: "arc"
|
||||
controllerServiceAccount.namespace: "arc-system"
|
||||
resource:
|
||||
all:
|
||||
metadata:
|
||||
annotations:
|
||||
unsafe-integer: 9007199254740993
|
||||
release:
|
||||
name: "test-name"
|
||||
namespace: "test-namespace"
|
||||
asserts:
|
||||
- failedTemplate:
|
||||
errorMessage: '.Values.resource.all.metadata.annotations: invalid value for annotation "unsafe-integer": unquoted integers outside the IEEE 754 safe range must be quoted to preserve their exact value'
|
||||
|
||||
- it: should fail when a metadata block is not a mapping
|
||||
set:
|
||||
@@ -201,7 +291,28 @@ tests:
|
||||
- failedTemplate:
|
||||
errorMessage: '.Values.runner.pod.metadata.annotations: invalid value for annotation "nested": must be a scalar, got map. Quote the value if it is meant to be a string'
|
||||
|
||||
- it: should accept a prefix segment longer than 63 characters, matching Kubernetes
|
||||
- it: should render a metadata prefix segment of exactly 63 characters
|
||||
set:
|
||||
scaleset.name: "test"
|
||||
auth.url: "https://github.com/org"
|
||||
auth.githubToken: "gh_token12345"
|
||||
controllerServiceAccount.name: "arc"
|
||||
controllerServiceAccount.namespace: "arc-system"
|
||||
runner:
|
||||
pod:
|
||||
metadata:
|
||||
labels:
|
||||
? "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com/purpose"
|
||||
: "yes"
|
||||
release:
|
||||
name: "test-name"
|
||||
namespace: "test-namespace"
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.template.metadata.labels["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com/purpose"]
|
||||
value: "yes"
|
||||
|
||||
- it: should fail when a metadata prefix segment is longer than 63 characters
|
||||
set:
|
||||
scaleset.name: "test"
|
||||
auth.url: "https://github.com/org"
|
||||
@@ -218,9 +329,8 @@ tests:
|
||||
name: "test-name"
|
||||
namespace: "test-namespace"
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.template.metadata.labels["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com/purpose"]
|
||||
value: "yes"
|
||||
- failedTemplate:
|
||||
errorMessage: '.Values.runner.pod.metadata.labels: invalid label key "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.example.com/purpose": the prefix segment "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" must be no more than 63 characters'
|
||||
|
||||
# A listener pod template carrying both metadata and spec used to render "true" and the
|
||||
# following "spec:" key onto the same line, producing invalid YAML.
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValuesExamplesAreTopLevel(t *testing.T) {
|
||||
valuesPath, err := filepath.Abs("../values.yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
values, err := os.ReadFile(valuesPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
marker string
|
||||
example string
|
||||
}{
|
||||
{
|
||||
name: "proxy",
|
||||
marker: "## Proxy can be used to define proxy settings",
|
||||
example: "# proxy:",
|
||||
},
|
||||
{
|
||||
name: "github server TLS",
|
||||
marker: "## A self-signed CA certificate for communication with the GitHub server",
|
||||
example: "# githubServerTLS:",
|
||||
},
|
||||
} {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
markerIndex := strings.Index(string(values), test.marker)
|
||||
if markerIndex == -1 {
|
||||
t.Fatalf("could not find example marker %q", test.marker)
|
||||
}
|
||||
|
||||
exampleIndex := markerIndex + strings.Index(string(values[markerIndex:]), test.example)
|
||||
if exampleIndex < markerIndex {
|
||||
t.Fatalf("could not find example %q after marker %q", test.example, test.marker)
|
||||
}
|
||||
|
||||
lineStart := strings.LastIndex(string(values[:exampleIndex]), "\n") + 1
|
||||
lineEnd := exampleIndex + strings.Index(string(values[exampleIndex:]), "\n")
|
||||
if got := string(values[lineStart:lineEnd]); got != test.example {
|
||||
t.Errorf("example must be top-level: got %q, want %q", got, test.example)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -62,20 +62,41 @@ secretResolution:
|
||||
# tenant_id: ""
|
||||
# certificate_path: ""
|
||||
|
||||
## Proxy can be used to define proxy settings that will be used by the
|
||||
## controller, the listener and the runner of this scale set.
|
||||
# proxy:
|
||||
# http:
|
||||
# url: http://proxy.com:1234
|
||||
# credentialSecretRef: proxy-auth # a secret with `username` and `password` keys
|
||||
# https:
|
||||
# url: http://proxy.com:1234
|
||||
# credentialSecretRef: proxy-auth # a secret with `username` and `password` keys
|
||||
# noProxy:
|
||||
# - example.com
|
||||
# - example.org
|
||||
## Proxy can be used to define proxy settings that will be used by the
|
||||
## controller, the listener and the runner of this scale set.
|
||||
# proxy:
|
||||
# http:
|
||||
# url: http://proxy.com:1234
|
||||
# credentialSecretRef: proxy-auth # a secret with `username` and `password` keys
|
||||
# https:
|
||||
# url: http://proxy.com:1234
|
||||
# credentialSecretRef: proxy-auth # a secret with `username` and `password` keys
|
||||
# noProxy:
|
||||
# - example.com
|
||||
# - example.org
|
||||
|
||||
## Resource object allows modifying resources created by the chart itself
|
||||
## A self-signed CA certificate for communication with the GitHub server can be
|
||||
## provided using a config map key selector. If `runnerMountPath` is set, for
|
||||
## each runner pod ARC will:
|
||||
## - create a `github-server-tls-cert` volume containing the certificate
|
||||
## specified in `certificateFrom`
|
||||
## - mount that volume on path `runnerMountPath`/{certificate name}
|
||||
## - set NODE_EXTRA_CA_CERTS environment variable to that same path
|
||||
## - set RUNNER_UPDATE_CA_CERTS environment variable to "1" (as of version
|
||||
## 2.303.0 this will instruct the runner to reload certificates on the host)
|
||||
##
|
||||
## If any of the above had already been set by the user in the runner pod
|
||||
## template, ARC will observe those and not overwrite them.
|
||||
## Example configuration:
|
||||
#
|
||||
# githubServerTLS:
|
||||
# certificateFrom:
|
||||
# configMapKeyRef:
|
||||
# name: config-map-name
|
||||
# key: ca.crt
|
||||
# runnerMountPath: /usr/local/share/ca-certificates/
|
||||
|
||||
## Resource object allows modifying resources created by the chart itself
|
||||
resource:
|
||||
# Specifies metadata that will be applied to all resources managed by ARC
|
||||
all:
|
||||
@@ -269,27 +290,6 @@ runner:
|
||||
# spec:
|
||||
# containers: []
|
||||
|
||||
## A self-signed CA certificate for communication with the GitHub server can be
|
||||
## provided using a config map key selector. If `runnerMountPath` is set, for
|
||||
## each runner pod ARC will:
|
||||
## - create a `github-server-tls-cert` volume containing the certificate
|
||||
## specified in `certificateFrom`
|
||||
## - mount that volume on path `runnerMountPath`/{certificate name}
|
||||
## - set NODE_EXTRA_CA_CERTS environment variable to that same path
|
||||
## - set RUNNER_UPDATE_CA_CERTS environment variable to "1" (as of version
|
||||
## 2.303.0 this will instruct the runner to reload certificates on the host)
|
||||
##
|
||||
## If any of the above had already been set by the user in the runner pod
|
||||
## template, ARC will observe those and not overwrite them.
|
||||
## Example configuration:
|
||||
#
|
||||
# githubServerTLS:
|
||||
# certificateFrom:
|
||||
# configMapKeyRef:
|
||||
# name: config-map-name
|
||||
# key: ca.crt
|
||||
# runnerMountPath: /usr/local/share/ca-certificates/
|
||||
|
||||
## controllerServiceAccount is the service account of the controller
|
||||
controllerServiceAccount:
|
||||
namespace: ""
|
||||
|
||||
Reference in New Issue
Block a user