Harden SDK docker runtime cache

This commit is contained in:
Snider 2026-04-15 12:42:15 +01:00
parent cbe1c419e1
commit 820323abd5
2 changed files with 56 additions and 7 deletions

View file

@ -2,6 +2,10 @@ package generators
import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
stdio "io"
"strconv"
"sync"
"time"
@ -20,6 +24,8 @@ var (
var availabilityProbeTimeout = 2 * time.Second
const dockerRuntimeFingerprintBytes = 4 * 1024
func dockerRuntimeAvailable() bool {
ctx, cancel := availabilityProbeContext()
defer cancel()
@ -50,6 +56,9 @@ func dockerRuntimeAvailableWithContext(ctx context.Context) bool {
if err != nil && ctx.Err() != nil {
return false
}
if ctx.Err() != nil {
return false
}
available := err == nil
storeDockerRuntimeAvailability(dockerCommand, commandState, available)
@ -84,9 +93,22 @@ func dockerRuntimeCommandState(command string) (string, error) {
return "", err
}
file, err := ax.Open(command)
if err != nil {
return "", err
}
defer func() { _ = file.Close() }()
hasher := sha256.New()
if _, err := stdio.CopyN(hasher, file, dockerRuntimeFingerprintBytes); err != nil && !errors.Is(err, stdio.EOF) {
return "", err
}
return command + "|" +
strconv.FormatInt(info.Size(), 10) + "|" +
strconv.FormatInt(info.ModTime().UnixNano(), 10), nil
strconv.FormatInt(info.ModTime().UnixNano(), 10) + "|" +
info.Mode().String() + "|" +
hex.EncodeToString(hasher.Sum(nil)), nil
}
func cachedDockerRuntimeAvailability(command, state string) (bool, bool) {
@ -111,3 +133,13 @@ func storeDockerRuntimeAvailability(command, state string, available bool) {
dockerRuntimeOK = available
dockerRuntimeChecked = true
}
func resetDockerRuntimeAvailabilityCache() {
dockerRuntimeMu.Lock()
defer dockerRuntimeMu.Unlock()
dockerRuntimeChecked = false
dockerRuntimeOK = false
dockerRuntimeCommand = ""
dockerRuntimeState = ""
}

View file

@ -2,8 +2,8 @@ package generators
import (
"context"
"os"
"strings"
"sync"
"testing"
"time"
@ -13,11 +13,7 @@ import (
)
func resetDockerRuntimeState() {
dockerRuntimeMu = sync.Mutex{}
dockerRuntimeChecked = false
dockerRuntimeOK = false
dockerRuntimeCommand = ""
dockerRuntimeState = ""
resetDockerRuntimeAvailabilityCache()
}
func setAvailabilityProbeTimeout(t *testing.T, timeout time.Duration) {
@ -197,3 +193,24 @@ func TestSDK_DockerRuntimeAvailabilityInvalidatesCachedSuccessWhenCommandMutates
assert.False(t, dockerRuntimeAvailable())
}
func TestSDK_DockerRuntimeAvailabilityInvalidatesCachedSuccessWhenCommandKeepsSizeAndMTime_Good(t *testing.T) {
resetDockerRuntimeState()
t.Cleanup(resetDockerRuntimeState)
dockerDir := t.TempDir()
successScript := "#!/bin/sh\nif [ \"$1\" = \"--help\" ]; then\n exit 0\nfi\nexit 0\n"
failureScript := "#!/bin/sh\nif [ \"$1\" = \"--help\" ]; then\n exit 1\nfi\nexit 0\n"
dockerPath := writeFakeDockerRuntime(t, dockerDir, successScript)
t.Setenv("PATH", dockerDir)
assert.True(t, dockerRuntimeAvailable())
info, err := os.Stat(dockerPath)
require.NoError(t, err)
require.NoError(t, ax.WriteFile(dockerPath, []byte(failureScript), 0o755))
require.NoError(t, os.Chtimes(dockerPath, info.ModTime(), info.ModTime()))
assert.False(t, dockerRuntimeAvailable())
}