mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-25 13:10:03 +00:00
02e53bc777
Go 1.26.10 (20 October) distrusts the compromised eMudhra intermediate CAs in crypto/x509 (CVE-2026-111943). Binaries built with 1.26.9 still accept chains through them, so every verified outbound TLS connection Pulse makes (licence, updates, webhooks, AI providers) stays open to interception by a holder of those keys. govulncheck does not flag a trust-store change as a called symbol, so the scheduled scan stayed green while the gap existed. Advance the governed toolchain floor and every pin together: go.mod, the toolchain installer and release-script guards, the source-built container stages (manifest-list digests for golang:1.26.10-alpine and the Debian dev container image), the root-pair diagnostic, and their tests. Vite 6.4.3 bundles a launch-editor with three advisories (GHSA-587h-7wmj-h5pm, GHSA-r825-w8mm-5pxw, GHSA-698v-4r3x-3vcv) that failed the daily advisory watch. 6.4.4 bundles the fixed launch-editor and 6.4.5 adds the dev-server server.fs query check. Both reach only the local dev server, not the built product. The lockfile changes only the vite entry, and dependencySecurity.test.ts now pins the floor.
77 lines
3.6 KiB
Go
77 lines
3.6 KiB
Go
package installtests
|
|
|
|
import (
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestIntegrationContainersUseGovernedImmutableBases(t *testing.T) {
|
|
dockerfileBytes, err := os.ReadFile(repoFile("tests", "integration", "mock-github-server", "Dockerfile"))
|
|
if err != nil {
|
|
t.Fatalf("read mock GitHub Dockerfile: %v", err)
|
|
}
|
|
dockerfile := string(dockerfileBytes)
|
|
assertDigestPinnedDockerStage(t, dockerfile, `FROM golang:1.26.10-alpine@sha256:`, ` AS builder`)
|
|
assertDigestPinnedDockerStage(t, dockerfile, `FROM alpine:3.24@sha256:`, ``)
|
|
|
|
composeBytes, err := os.ReadFile(repoFile("tests", "integration", "docker-compose.test.yml"))
|
|
if err != nil {
|
|
t.Fatalf("read integration compose file: %v", err)
|
|
}
|
|
compose := string(composeBytes)
|
|
if !regexpDigestPinnedImage(`alpine:3.24`, compose) {
|
|
t.Fatal("integration seed image must use the governed Alpine line pinned by a full immutable digest")
|
|
}
|
|
if strings.Contains(dockerfile, "golang:1.23-alpine") || strings.Contains(compose, "image: alpine:3.20\n") {
|
|
t.Fatal("integration containers must not restore retired toolchain or runtime lines")
|
|
}
|
|
}
|
|
|
|
func regexpDigestPinnedImage(image string, content string) bool {
|
|
pin := regexp.QuoteMeta(image) + `@sha256:[0-9a-f]{64}`
|
|
// The same-run CI bundle uses this fixed override; the local default must
|
|
// still be a complete governed pin. Do not admit arbitrary interpolation.
|
|
pattern := regexp.MustCompile(`(?m)^[ \t]*image:[ \t]*(?:` + pin + `|\$\{PULSE_E2E_SEED_IMAGE:-` + pin + `\})[ \t]*\r?$`)
|
|
return pattern.MatchString(content)
|
|
}
|
|
|
|
func TestIntegrationContainerImagePinForms(t *testing.T) {
|
|
pin := "alpine:3.24@sha256:" + strings.Repeat("a", 64)
|
|
for _, tc := range []struct {
|
|
name string
|
|
value string
|
|
want bool
|
|
}{
|
|
{"literal", "image: " + pin + "\n", true},
|
|
{"indented-literal", " image: " + pin + "\n", true},
|
|
{"same-run-override", " image: ${PULSE_E2E_SEED_IMAGE:-" + pin + "}\n", true},
|
|
{"crlf-override", "\timage:\t${PULSE_E2E_SEED_IMAGE:-" + pin + "} \r\n", true},
|
|
{"unpinned-literal", "image: alpine:3.24\n", false},
|
|
{"unpinned-default", "image: ${PULSE_E2E_SEED_IMAGE:-alpine:3.24}\n", false},
|
|
{"missing-default", "image: ${PULSE_E2E_SEED_IMAGE}\n", false},
|
|
{"retired-line", "image: ${PULSE_E2E_SEED_IMAGE:-" + strings.Replace(pin, "3.24", "3.20", 1) + "}\n", false},
|
|
{"mutable-line", "image: ${PULSE_E2E_SEED_IMAGE:-" + strings.Replace(pin, "3.24", "latest", 1) + "}\n", false},
|
|
{"other-variable", "image: ${OTHER_IMAGE:-" + pin + "}\n", false},
|
|
{"unset-only-default", "image: ${PULSE_E2E_SEED_IMAGE-" + pin + "}\n", false},
|
|
{"alternate-operator", "image: ${PULSE_E2E_SEED_IMAGE:+" + pin + "}\n", false},
|
|
{"short-literal-digest", "image: " + pin[:len(pin)-1] + "\n", false},
|
|
{"short-default-digest", "image: ${PULSE_E2E_SEED_IMAGE:-" + pin[:len(pin)-1] + "}\n", false},
|
|
{"long-default-digest", "image: ${PULSE_E2E_SEED_IMAGE:-" + pin + "a}\n", false},
|
|
{"invalid-default-digest", "image: ${PULSE_E2E_SEED_IMAGE:-" + strings.Replace(pin, "sha256:a", "sha256:g", 1) + "}\n", false},
|
|
{"missing-close", "image: ${PULSE_E2E_SEED_IMAGE:-" + pin + "\n", false},
|
|
{"default-suffix", "image: ${PULSE_E2E_SEED_IMAGE:-" + pin + ":other}\n", false},
|
|
{"override-suffix", "image: ${PULSE_E2E_SEED_IMAGE:-" + pin + "}/other\n", false},
|
|
{"multiline-value", "image:\n" + pin + "\n", false},
|
|
{"multiline-default", "image: ${PULSE_E2E_SEED_IMAGE:-\n" + pin + "}\n", false},
|
|
{"commented-pin", "# image: " + pin + "\nimage: alpine:latest\n", false},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := regexpDigestPinnedImage("alpine:3.24", tc.value); got != tc.want {
|
|
t.Fatalf("governed seed image admission = %t, want %t", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|