mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-26 05:29:45 +00:00
Preserve the current upstream Go security toolchain and dependency repair
Compose the exact observed upstream additively without rewriting reviewed tip 805ea6d712 or any accepted ancestry. The new Go 1.26.9 and x/net graph need matching final-source evidence before this batch can be proposed.
Change-source: pulse-maintainer
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.26.8@sha256:0f063af2d465d8dcae54cce04278ada488b96f77b42449c8d071e47d016cc65a
|
||||
FROM golang:1.26.9@sha256:d7722066f0b60ceccb6c0643cbed1f5f9e15506ac237146c95333504d7805d89
|
||||
|
||||
# Set bash as default shell for features
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
@@ -5,7 +5,7 @@ This dev container provides a complete, reproducible development environment for
|
||||
## What's Included
|
||||
|
||||
### Development Tools
|
||||
- **Go 1.26.8** - Backend development
|
||||
- **Go 1.26.9** - Backend development
|
||||
- **Node.js 24** - Frontend development
|
||||
- **gopls v0.17.0** - Go language server
|
||||
- **Delve** - Go debugger
|
||||
@@ -225,7 +225,7 @@ Custom overrides: Create `.env.devcontainer` (gitignored)
|
||||
## Resources
|
||||
|
||||
- **VM Specs**: 8GB RAM, 30GB disk, 2 CPU cores
|
||||
- **Base Image**: `golang:1.26.8` (Debian-based)
|
||||
- **Base Image**: `golang:1.26.9` (Debian-based)
|
||||
- **Caches**: ~2-3GB for Go modules and build artifacts
|
||||
|
||||
## Tips & Tricks
|
||||
@@ -247,7 +247,7 @@ MacBook (VS Code)
|
||||
dev-containers VM (Proxmox)
|
||||
↓ Docker
|
||||
Dev Container
|
||||
├── Go 1.26.8 + tools
|
||||
├── Go 1.26.9 + tools
|
||||
├── Node 24 + npm
|
||||
├── Your code (/workspaces/pulse)
|
||||
├── Hot reload watchers
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
|
||||
with:
|
||||
go-version: '1.26.8'
|
||||
go-version: '1.26.9'
|
||||
cache: false
|
||||
- name: Collect fixed exact-pair root evidence
|
||||
run: python3 scripts/diagnose-root-pair.py --output root-pair-evidence
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ RUN --mount=type=cache,id=pulse-npm-cache,target=/root/.npm \
|
||||
# Build stage for Go backend
|
||||
# Force amd64 platform - Go cross-compiles for all targets anyway,
|
||||
# and this avoids slow QEMU emulation during multi-arch builds
|
||||
FROM --platform=linux/amd64 golang:1.26.8-alpine@sha256:51a7c389a5ddaf82f527191a1e9bff9928655130a44e4975dd1d7e0acf59f1ae AS backend-builder
|
||||
FROM --platform=linux/amd64 golang:1.26.9-alpine@sha256:3082400e369fa24d5fc60bca20edab3f6d604e0c5a690ec66b295eff4dd87ade AS backend-builder
|
||||
|
||||
ARG BUILD_AGENT
|
||||
ARG VERSION
|
||||
|
||||
@@ -12,7 +12,7 @@ COPY SECURITY.md TERMS.md /app/
|
||||
RUN --mount=type=cache,id=pulse-control-plane-npm-cache,target=/root/.npm \
|
||||
npm run build
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26.8-alpine@sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26.9-alpine@sha256:3082400e369fa24d5fc60bca20edab3f6d604e0c5a690ec66b295eff4dd87ade AS builder
|
||||
ARG VERSION=dev
|
||||
ARG BUILD_TIME=unknown
|
||||
ARG GIT_COMMIT=unknown
|
||||
|
||||
@@ -10,7 +10,7 @@ The source pair is fixed in `scripts/diagnose-root-pair.py`: baseline
|
||||
`1120cce4d631bca5525cafc5b5620251998773f7` tree measured from ephemeral
|
||||
source commit `4cdf250f474ad9cde6ffb121527f021b7387603f7`; metadata retains that original
|
||||
evidence identity. Tree and HTTP source hashes must match. Both API test binaries
|
||||
use Go 1.26.8, readonly modules and identical CI
|
||||
use Go 1.26.9, readonly modules and identical CI
|
||||
embed text; compilation finishes before either warm-up. Four available CPUs
|
||||
are selected and inherited by children, GOMAXPROCS is four. After two 100ms
|
||||
warm-ups and ten seconds settling, ten rounds alternate AB/BA with one 1s
|
||||
|
||||
@@ -4871,18 +4871,20 @@ governed release toolchain and immutable Go builder digest together, not to
|
||||
suppress the scanner or produce release artifacts with an older patched-over
|
||||
runtime.
|
||||
The hosted control-plane source builder and integration mock builder are both
|
||||
on the reviewed `golang:1.26.8-alpine` digest
|
||||
`sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c`.
|
||||
on the reviewed `golang:1.26.9-alpine` digest
|
||||
`sha256:3082400e369fa24d5fc60bca20edab3f6d604e0c5a690ec66b295eff4dd87ade`.
|
||||
`TestProviderMSPGoBuilderMatchesIntegrationMock` pins their exact agreement;
|
||||
it does not pull the image, prove its registry contents, or qualify a deployed
|
||||
control plane. The release publisher's prebuilt control-plane target is a
|
||||
separate path and does not rebuild its binary from this stage.
|
||||
As of 2026-08-27, the governed release floor is Go `1.26.7`. It supersedes
|
||||
`1.26.5`, whose standard library is reachable through seven vulnerable Pulse
|
||||
call paths reported by `govulncheck`, including HTTP/TLS, URL parsing, SAML XML
|
||||
decoding, HTML templating, and public-key parsing. Both source-built container
|
||||
stages pin the Docker Official Images Linux amd64 manifest
|
||||
`sha256:28d89ee9cc0ff9fec75c82ca201e6bf7fdf9a679d4b7b24dfa04f2bb766bb468`;
|
||||
As of 2026-10-11, the governed release floor is Go `1.26.9`. It supersedes
|
||||
`1.26.8`, whose standard library is reachable through twelve vulnerable Pulse
|
||||
call paths reported by `govulncheck` after the 2026-10-08 advisories, including
|
||||
`net/http`, `crypto/tls`, `html/template`, `os`, and `net/textproto`. The
|
||||
`golang.org/x/net` floor is `v0.60.0` for the same advisories. Source-built
|
||||
container stages pin the Docker Official Images manifest list for
|
||||
`golang:1.26.9-alpine`,
|
||||
`sha256:3082400e369fa24d5fc60bca20edab3f6d604e0c5a690ec66b295eff4dd87ade`;
|
||||
the checked-in toolchain files and release-script guards must reject an older
|
||||
compiler so local, exact-candidate, provider control-plane, and container builds
|
||||
cannot silently reintroduce the vulnerable runtime.
|
||||
|
||||
@@ -2,7 +2,7 @@ module github.com/rcourtman/pulse-go-rewrite
|
||||
|
||||
go 1.26.0
|
||||
|
||||
toolchain go1.26.8
|
||||
toolchain go1.26.9
|
||||
|
||||
require (
|
||||
github.com/IGLOU-EU/go-wildcard/v2 v2.1.1
|
||||
@@ -107,7 +107,7 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
golang.org/x/net v0.58.0 // indirect
|
||||
golang.org/x/net v0.60.0 // indirect
|
||||
golang.org/x/text v0.42.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
google.golang.org/protobuf v1.36.12 // indirect
|
||||
|
||||
@@ -288,6 +288,8 @@ golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
golang.org/x/net v0.60.0 h1:79p50tfZlm0J9YfoDsSi639qSXNGVwEzOPLCxM2FsYU=
|
||||
golang.org/x/net v0.60.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/oauth2 v0.37.0 h1:JUlcxA8oAtauLfiH8FX2/FkAWHAdi0QtGCGc+hofE98=
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
go1.26.8
|
||||
go1.26.9
|
||||
|
||||
@@ -44,7 +44,7 @@ case "${OUTPUT_ROOT}" in
|
||||
;;
|
||||
esac
|
||||
|
||||
required_go="go1.26.8"
|
||||
required_go="go1.26.9"
|
||||
current_go="$(go env GOVERSION 2>/dev/null || true)"
|
||||
if [[ "${current_go}" != "${required_go}" ]]; then
|
||||
echo "Error: Go toolchain must be ${required_go} (got ${current_go:-unknown})." >&2
|
||||
|
||||
@@ -20,7 +20,7 @@ if [ -x /usr/local/go/bin/go ]; then
|
||||
fi
|
||||
|
||||
# Release artifacts must be built with the vetted toolchain to match security-gate evidence.
|
||||
required_go="go1.26.8"
|
||||
required_go="go1.26.9"
|
||||
current_go="$(go env GOVERSION 2>/dev/null || true)"
|
||||
if [[ "${PULSE_SKIP_GO_VERSION_CHECK:-false}" != "true" ]]; then
|
||||
if [[ "${current_go}" != "${required_go}" ]]; then
|
||||
|
||||
@@ -35,7 +35,7 @@ if [[ -z "${PULSE_UPDATE_SIGNING_PUBLIC_KEY:-}" ]]; then
|
||||
exit 3
|
||||
fi
|
||||
|
||||
required_go="go1.26.8"
|
||||
required_go="go1.26.9"
|
||||
go_version="$(go env GOVERSION 2>/dev/null || true)"
|
||||
if [[ "${go_version}" != "${required_go}" ]]; then
|
||||
echo "Error: Go toolchain must be ${required_go} (got ${go_version:-unknown})." >&2
|
||||
|
||||
@@ -71,8 +71,8 @@ def telemetry():
|
||||
def execute(repo, output):
|
||||
output.mkdir(parents=True, exist_ok=False) # never overwrite a previous attempt
|
||||
os.environ.update(GOTOOLCHAIN='local', GOMAXPROCS='4', GOFLAGS='-mod=readonly', GOENV='off', GOAMD64='v1', CGO_ENABLED='1')
|
||||
if run(['go', 'version']).strip() != 'go version go1.26.8 linux/amd64':
|
||||
raise ValueError('requires exact hosted experiment toolchain go1.26.8 linux/amd64')
|
||||
if run(['go', 'version']).strip() != 'go version go1.26.9 linux/amd64':
|
||||
raise ValueError('requires exact hosted experiment toolchain go1.26.9 linux/amd64')
|
||||
metadata = {
|
||||
'purpose': 'diagnostic-only; no gate disposition or release candidate',
|
||||
'rounds': ROUNDS, 'gomaxprocs': 4, 'benchtime': '1s',
|
||||
|
||||
@@ -3,7 +3,7 @@ set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
VERSION_FILE="${SCRIPT_DIR}/.go-version"
|
||||
DEFAULT_VERSION="go1.26.8"
|
||||
DEFAULT_VERSION="go1.26.9"
|
||||
TARGET_ROOT="/opt/toolchains/go"
|
||||
DOWNLOAD_ROOT="https://dl.google.com/go"
|
||||
GOPATH_DIR="/var/lib/pulse/go"
|
||||
@@ -14,7 +14,8 @@ if [[ ! -f "$VERSION_FILE" ]]; then
|
||||
echo "$DEFAULT_VERSION" | sudo tee "$VERSION_FILE" >/dev/null
|
||||
fi
|
||||
VERSION="$(tr -d '
|
||||
|
||||
|
||||
' < "$VERSION_FILE")"
|
||||
ARCHIVE="${VERSION}.linux-amd64.tar.gz"
|
||||
DOWNLOAD_DIR="${TMPDIR:-/tmp}/go-install"
|
||||
ARCHIVE_PATH="$DOWNLOAD_DIR/$ARCHIVE"
|
||||
@@ -28,7 +29,8 @@ fi
|
||||
curl -fsSL "$DOWNLOAD_ROOT/$ARCHIVE.sha256" -o "$SHA_PATH"
|
||||
|
||||
CHECKSUM="$(tr -d '
|
||||
CHECKSUM="$(tr -d '
|
||||
|
||||
' < "$SHA_PATH")"
|
||||
printf '%s %s
|
||||
' "$CHECKSUM" "$ARCHIVE_PATH" | sha256sum -c -
|
||||
|
||||
|
||||
@@ -1618,7 +1618,7 @@ func TestDockerBuildUsesCanonicalReleaseLdflags(t *testing.T) {
|
||||
dockerfile := string(dockerfileBytes)
|
||||
dockerRequired := []string{
|
||||
"FROM --platform=linux/amd64 node:24-alpine@sha256:" + node24Amd64FrontendDigest + " AS frontend-builder",
|
||||
`FROM --platform=linux/amd64 golang:1.26.8-alpine@sha256:`,
|
||||
`FROM --platform=linux/amd64 golang:1.26.9-alpine@sha256:`,
|
||||
`FROM backend-builder AS release-assets-builder`,
|
||||
`AS agent_runtime`,
|
||||
`AS pulse-runtime-foundation`,
|
||||
@@ -1653,7 +1653,7 @@ func TestDockerBuildUsesCanonicalReleaseLdflags(t *testing.T) {
|
||||
}
|
||||
}
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM --platform=linux/amd64 node:24-alpine@sha256:`, ` AS frontend-builder`)
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM --platform=linux/amd64 golang:1.26.8-alpine@sha256:`, ` AS backend-builder`)
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM --platform=linux/amd64 golang:1.26.9-alpine@sha256:`, ` AS backend-builder`)
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM alpine:3.24@sha256:`, ` AS agent_runtime`)
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM alpine:3.24@sha256:`, ` AS pulse-runtime-foundation`)
|
||||
hostedStart := strings.Index(dockerfile, `FROM pulse-runtime-base AS hosted_runtime`)
|
||||
@@ -1666,7 +1666,7 @@ func TestDockerBuildUsesCanonicalReleaseLdflags(t *testing.T) {
|
||||
t.Fatalf("hosted_runtime target must not depend on installer rendering or embedded agent artifacts:\n%s", hostedStage)
|
||||
}
|
||||
if strings.Contains(dockerfile, `FROM --platform=linux/amd64 node:24-alpine AS frontend-builder`) ||
|
||||
strings.Contains(dockerfile, `FROM --platform=linux/amd64 golang:1.26.8-alpine AS backend-builder`) ||
|
||||
strings.Contains(dockerfile, `FROM --platform=linux/amd64 golang:1.26.9-alpine AS backend-builder`) ||
|
||||
strings.Contains(dockerfile, `FROM alpine:3.24 AS agent_runtime`) ||
|
||||
strings.Contains(dockerfile, `FROM alpine:3.24 AS pulse-runtime-base`) {
|
||||
t.Fatal("Dockerfile base images must be pinned by immutable @sha256 digests")
|
||||
|
||||
@@ -13,7 +13,7 @@ func TestIntegrationContainersUseGovernedImmutableBases(t *testing.T) {
|
||||
t.Fatalf("read mock GitHub Dockerfile: %v", err)
|
||||
}
|
||||
dockerfile := string(dockerfileBytes)
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM golang:1.26.8-alpine@sha256:`, ` AS builder`)
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM golang:1.26.9-alpine@sha256:`, ` AS builder`)
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM alpine:3.24@sha256:`, ``)
|
||||
|
||||
composeBytes, err := os.ReadFile(repoFile("tests", "integration", "docker-compose.test.yml"))
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestProviderMSPGoBuilderMatchesIntegrationMock(t *testing.T) {
|
||||
// These builders use the same Go patch level and immutable image. The
|
||||
// mock's build must not exercise a different compiler from the hosted
|
||||
// control plane merely because Dependabot advanced one Dockerfile.
|
||||
const expected = "golang:1.26.8-alpine@sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c"
|
||||
const expected = "golang:1.26.9-alpine@sha256:3082400e369fa24d5fc60bca20edab3f6d604e0c5a690ec66b295eff4dd87ade"
|
||||
pattern := regexp.MustCompile(`(?m)^FROM (?:--platform=\$BUILDPLATFORM )?(golang:[^[:space:]]+) AS builder$`)
|
||||
for _, path := range [][]string{
|
||||
{"deploy", "provider-msp", "Dockerfile.control-plane"},
|
||||
@@ -328,7 +328,7 @@ func TestProviderMSPControlPlaneDockerfileBuildsReleaseLicenseBinary(t *testing.
|
||||
"FROM --platform=linux/amd64 node:24-alpine@sha256:"+node24Amd64FrontendDigest+" AS frontend-builder",
|
||||
"npm ci",
|
||||
"npm run build",
|
||||
"FROM --platform=$BUILDPLATFORM golang:1.26.8-alpine@sha256:",
|
||||
"FROM --platform=$BUILDPLATFORM golang:1.26.9-alpine@sha256:",
|
||||
"FROM alpine:3.24@sha256:",
|
||||
"ARG PULSE_LICENSE_PUBLIC_KEY_SHA256",
|
||||
"ARG TARGETOS",
|
||||
@@ -355,7 +355,7 @@ func TestProviderMSPControlPlaneDockerfileBuildsReleaseLicenseBinary(t *testing.
|
||||
"CGO_ENABLED=0 go build -o /pulse-control-plane ./cmd/pulse-control-plane",
|
||||
)
|
||||
assertDigestPinnedDockerStage(t, text, `FROM --platform=linux/amd64 node:24-alpine@sha256:`, ` AS frontend-builder`)
|
||||
assertDigestPinnedDockerStage(t, text, `FROM --platform=$BUILDPLATFORM golang:1.26.8-alpine@sha256:`, ` AS builder`)
|
||||
assertDigestPinnedDockerStage(t, text, `FROM --platform=$BUILDPLATFORM golang:1.26.9-alpine@sha256:`, ` AS builder`)
|
||||
assertDigestPinnedDockerStage(t, text, `FROM alpine:3.24@sha256:`, ` AS control-plane-runtime-foundation`)
|
||||
}
|
||||
|
||||
|
||||
@@ -414,9 +414,9 @@ test_go_release_toolchain_floor() {
|
||||
module_toolchain="$(sed -n 's/^toolchain //p' "${ROOT_DIR}/go.mod")"
|
||||
installed_toolchain="$(cd "${ROOT_DIR}" && go env GOVERSION)"
|
||||
|
||||
assert_contains "Go module uses the patched release toolchain" "${module_toolchain}" "go1.26.8"
|
||||
assert_contains "dev toolchain installer uses the patched release toolchain" "$(<"${ROOT_DIR}/scripts/.go-version")" "go1.26.8"
|
||||
assert_contains "active dev runtime resolves the patched release toolchain" "${installed_toolchain}" "go1.26.8"
|
||||
assert_contains "Go module uses the patched release toolchain" "${module_toolchain}" "go1.26.9"
|
||||
assert_contains "dev toolchain installer uses the patched release toolchain" "$(<"${ROOT_DIR}/scripts/.go-version")" "go1.26.9"
|
||||
assert_contains "active dev runtime resolves the patched release toolchain" "${installed_toolchain}" "go1.26.9"
|
||||
}
|
||||
|
||||
test_backend_race_suite_keeps_hosted_runner_timeout_headroom() {
|
||||
|
||||
@@ -56,7 +56,7 @@ class DiagnosticTests(unittest.TestCase):
|
||||
|
||||
def fake_run(args, cwd=None):
|
||||
if args == ['go', 'version']:
|
||||
return 'go version go1.26.7 linux/amd64' if bad_go else 'go version go1.26.8 linux/amd64'
|
||||
return 'go version go1.26.7 linux/amd64' if bad_go else 'go version go1.26.9 linux/amd64'
|
||||
if args[:2] == ['git', 'rev-parse']:
|
||||
return 'wrong' if bad_tree else diag.TREES[args[2].split('^')[0]]
|
||||
if args[:3] == ['go', 'tool', 'nm']:
|
||||
@@ -151,7 +151,7 @@ class DiagnosticTests(unittest.TestCase):
|
||||
self.assertIn('[[ "$GITHUB_RUN_ATTEMPT" == 1 ]]', workflow)
|
||||
self.assertIn('path: root-pair-evidence/', workflow)
|
||||
self.assertIn('if: always()', workflow)
|
||||
self.assertIn("go-version: '1.26.8'", workflow)
|
||||
self.assertIn("go-version: '1.26.9'", workflow)
|
||||
self.assertIn('timeout-minutes: 45', workflow)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.26.8-alpine@sha256:8ac98ca534ac3f51e1f420a1dd2c15e74c75cfa0f23f3ad27eb5d7236c349a0c AS builder
|
||||
FROM golang:1.26.9-alpine@sha256:3082400e369fa24d5fc60bca20edab3f6d604e0c5a690ec66b295eff4dd87ade AS builder
|
||||
|
||||
WORKDIR /build
|
||||
COPY go.mod ./
|
||||
|
||||
Reference in New Issue
Block a user