mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Strengthen dependency maintenance contracts
This commit is contained in:
@@ -1478,9 +1478,10 @@ func TestDockerBuildUsesCanonicalReleaseLdflags(t *testing.T) {
|
||||
t.Fatalf("Dockerfile missing canonical release ldflags usage: %s", needle)
|
||||
}
|
||||
}
|
||||
assertDigestPinnedDockerBase(t, dockerfile, `FROM --platform=linux/amd64 node:20-alpine@sha256:`)
|
||||
assertDigestPinnedDockerBase(t, dockerfile, `FROM --platform=linux/amd64 golang:1.26.7-alpine@sha256:`)
|
||||
assertDigestPinnedDockerBase(t, dockerfile, `FROM alpine:3.20@sha256:`)
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM --platform=linux/amd64 node:20-alpine@sha256:`, ` AS frontend-builder`)
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM --platform=linux/amd64 golang:1.26.7-alpine@sha256:`, ` AS backend-builder`)
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM alpine:3.20@sha256:`, ` AS agent_runtime`)
|
||||
assertDigestPinnedDockerStage(t, dockerfile, `FROM alpine:3.20@sha256:`, ` AS pulse-runtime-foundation`)
|
||||
hostedStart := strings.Index(dockerfile, `FROM pulse-runtime-base AS hosted_runtime`)
|
||||
runtimeStart := strings.Index(dockerfile, `FROM pulse-runtime-base AS runtime`)
|
||||
if hostedStart == -1 || runtimeStart == -1 || hostedStart > runtimeStart {
|
||||
|
||||
@@ -5,12 +5,19 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// assertDigestPinnedDockerBase allows automated digest refreshes while still
|
||||
// rejecting shortened, malformed, or mutable base-image references.
|
||||
func assertDigestPinnedDockerBase(t *testing.T, dockerfile, prefix string) {
|
||||
// assertDigestPinnedDockerStage allows automated digest refreshes while still
|
||||
// rejecting shortened, malformed, mutable, or decoy base-image references for
|
||||
// the named production stage.
|
||||
func assertDigestPinnedDockerStage(t *testing.T, dockerfile, prefix, suffix string) {
|
||||
t.Helper()
|
||||
pattern := regexp.MustCompile(regexp.QuoteMeta(prefix) + `[0-9a-f]{64}(?:\s|$)`)
|
||||
pattern := regexp.MustCompile(
|
||||
`(?m)^` + regexp.QuoteMeta(prefix) + `[0-9a-f]{64}` + regexp.QuoteMeta(suffix) + `$`,
|
||||
)
|
||||
if !pattern.MatchString(dockerfile) {
|
||||
t.Fatalf("Dockerfile base image must use a full immutable digest: %s<64 lowercase hex characters>", prefix)
|
||||
t.Fatalf(
|
||||
"Dockerfile stage must use a full immutable digest: %s<64 lowercase hex characters>%s",
|
||||
prefix,
|
||||
suffix,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,9 +332,9 @@ func TestProviderMSPControlPlaneDockerfileBuildsReleaseLicenseBinary(t *testing.
|
||||
"FROM alpine:3.21",
|
||||
"CGO_ENABLED=0 go build -o /pulse-control-plane ./cmd/pulse-control-plane",
|
||||
)
|
||||
assertDigestPinnedDockerBase(t, text, `FROM --platform=linux/amd64 node:20-alpine@sha256:`)
|
||||
assertDigestPinnedDockerBase(t, text, `FROM --platform=$BUILDPLATFORM golang:1.26.7-alpine@sha256:`)
|
||||
assertDigestPinnedDockerBase(t, text, `FROM alpine:3.20@sha256:`)
|
||||
assertDigestPinnedDockerStage(t, text, `FROM --platform=linux/amd64 node:20-alpine@sha256:`, ` AS frontend-builder`)
|
||||
assertDigestPinnedDockerStage(t, text, `FROM --platform=$BUILDPLATFORM golang:1.26.7-alpine@sha256:`, ` AS builder`)
|
||||
assertDigestPinnedDockerStage(t, text, `FROM alpine:3.20@sha256:`, ` AS control-plane-runtime-foundation`)
|
||||
}
|
||||
|
||||
func assertContainsAll(t *testing.T, text string, required ...string) {
|
||||
|
||||
@@ -12,6 +12,18 @@ CONFIG = ROOT / ".github" / "dependabot.yml"
|
||||
SECURITY_SCAN = ROOT / ".github" / "workflows" / "security-scan.yml"
|
||||
|
||||
|
||||
def manifest_directories(filename: str) -> list[str]:
|
||||
"""Return repository-relative Dependabot directories for every manifest."""
|
||||
directories = set()
|
||||
for manifest in ROOT.rglob(filename):
|
||||
relative = manifest.relative_to(ROOT)
|
||||
if "node_modules" in relative.parts:
|
||||
continue
|
||||
parent = relative.parent.as_posix()
|
||||
directories.add("/" if parent == "." else f"/{parent}")
|
||||
return sorted(directories)
|
||||
|
||||
|
||||
class DependabotConfigTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls) -> None:
|
||||
@@ -26,17 +38,12 @@ class DependabotConfigTest(unittest.TestCase):
|
||||
)
|
||||
self.assertEqual(self.updates["github-actions"]["directory"], "/")
|
||||
self.assertEqual(
|
||||
self.updates["gomod"]["directories"],
|
||||
["/", "/tests/integration/mock-github-server"],
|
||||
sorted(self.updates["gomod"]["directories"]),
|
||||
manifest_directories("go.mod"),
|
||||
)
|
||||
self.assertEqual(
|
||||
self.updates["npm"]["directories"],
|
||||
[
|
||||
"/",
|
||||
"/frontend-modern",
|
||||
"/internal/cloudcp/portal/frontend",
|
||||
"/tests/integration",
|
||||
],
|
||||
sorted(self.updates["npm"]["directories"]),
|
||||
manifest_directories("package-lock.json"),
|
||||
)
|
||||
self.assertEqual(
|
||||
self.updates["docker"]["directories"],
|
||||
@@ -98,16 +105,17 @@ class DependabotConfigTest(unittest.TestCase):
|
||||
jobs = workflow["jobs"]
|
||||
self.assertEqual(
|
||||
set(jobs["govulncheck"]["strategy"]["matrix"]["directory"]),
|
||||
{".", "tests/integration/mock-github-server"},
|
||||
{
|
||||
"." if path == "/" else path.removeprefix("/")
|
||||
for path in manifest_directories("go.mod")
|
||||
},
|
||||
)
|
||||
npm_sets = jobs["npm-audit"]["strategy"]["matrix"]["include"]
|
||||
self.assertEqual(
|
||||
{item["directory"] for item in npm_sets},
|
||||
{
|
||||
".",
|
||||
"frontend-modern",
|
||||
"internal/cloudcp/portal/frontend",
|
||||
"tests/integration",
|
||||
"." if path == "/" else path.removeprefix("/")
|
||||
for path in manifest_directories("package-lock.json")
|
||||
},
|
||||
)
|
||||
scan_steps = jobs["npm-audit"]["steps"]
|
||||
|
||||
Reference in New Issue
Block a user