v0.281.0: 'Done' asks the probe first; sign-up closed after the first admin (decision 47); R-713 code-bound values refused, ${NAME|base64}
gates / gates (push) Successful in 23s
gates / gates (push) Successful in 23s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -37,7 +37,10 @@ func TestAfterInstall_ReplacesTheDefaultLoginOnceAndRecordsIt(t *testing.T) {
|
||||
|
||||
// 1. Success: the value is filled in, the command runs ONCE, the record says ok, the log never carries it.
|
||||
var calls [][]string
|
||||
m.afterLoadFn = func(_ string, args ...string) (string, error) { calls = append(calls, args); return "... FELHOM_OK", nil }
|
||||
m.afterLoadFn = func(_ string, args ...string) (string, error) {
|
||||
calls = append(calls, args)
|
||||
return "... FELHOM_OK", nil
|
||||
}
|
||||
cmd, err := expandAfterInstall(ai.Command, ai.Env, map[string]string{"ADMIN_PASSWORD": "Gen3r4tedValue"})
|
||||
must(t, err)
|
||||
if err := m.runAfterInstallNow("cloudapp", ai, cmd, record); err != nil {
|
||||
@@ -55,7 +58,10 @@ func TestAfterInstall_ReplacesTheDefaultLoginOnceAndRecordsIt(t *testing.T) {
|
||||
|
||||
// 2. No success marker: retried, then recorded as FAILED — never recorded ok.
|
||||
calls = nil
|
||||
m.afterLoadFn = func(_ string, args ...string) (string, error) { calls = append(calls, args); return "boom", errors.New("exit 1") }
|
||||
m.afterLoadFn = func(_ string, args ...string) (string, error) {
|
||||
calls = append(calls, args)
|
||||
return "boom", errors.New("exit 1")
|
||||
}
|
||||
if err := m.runAfterInstallNow("cloudapp", ai, cmd, record); err == nil {
|
||||
t.Fatal("a failing command reported success")
|
||||
}
|
||||
@@ -89,3 +95,34 @@ func TestAfterInstall_ReplacesTheDefaultLoginOnceAndRecordsIt(t *testing.T) {
|
||||
t.Fatal("an undeclared name was filled in")
|
||||
}
|
||||
}
|
||||
|
||||
// R-713 (v0.281.0): a value pasted into CODE must not be able to end a string or start an interpolation; the
|
||||
// same value as its own argument, a plain argument, or ${NAME|base64} is fine.
|
||||
// COMPANION RED-PROOF: make argumentShaped return true always → the claper-shaped case runs with the quote.
|
||||
func TestR713_AValueThatWouldBeReadAsCodeIsRefused(t *testing.T) {
|
||||
typed := map[string]string{"ADMIN_PASSWORD": `My"pass#{System.halt()}`}
|
||||
allowed := []string{"ADMIN_PASSWORD"}
|
||||
claperShaped := []string{"/app/bin/claper", "rpc", `Claper.x(u, %{password: "${ADMIN_PASSWORD}"})`}
|
||||
if _, err := expandAfterInstall(claperShaped, allowed, typed); err == nil || !strings.Contains(err.Error(), "read as code") {
|
||||
t.Fatalf("a quote and #{ went into Elixir code: %v", err)
|
||||
}
|
||||
for _, cmd := range [][]string{
|
||||
{"python3", "-c", "import sys; f(sys.argv[1])", "${ADMIN_PASSWORD}"}, // its own argument (mealie, wger)
|
||||
{"php", "artisan", "x", "--password=${ADMIN_PASSWORD}"}, // a plain argument (bookstack)
|
||||
{"python3", "cps.py", "-s", "admin:${ADMIN_PASSWORD}"}, // calibre-web
|
||||
{"/app/bin/claper", "rpc", `x(Base.decode64!("${ADMIN_PASSWORD|base64}"))`}, // claper, fixed
|
||||
} {
|
||||
out, err := expandAfterInstall(cmd, allowed, typed)
|
||||
if err != nil {
|
||||
t.Fatalf("%v: refused a safe placement: %v", cmd, err)
|
||||
}
|
||||
if strings.Contains(cmd[len(cmd)-1], "|base64") {
|
||||
if strings.ContainsAny(out[len(out)-1][len(`x(Base.decode64!("`):], `'\{}$`+"`") || strings.Contains(out[len(out)-1], `My"pass`) {
|
||||
t.Fatalf("base64 form leaked the raw value: %q", out[len(out)-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, err := expandAfterInstall([]string{"x", "${ADMIN_PASSWORD|rot13}"}, allowed, typed); err == nil {
|
||||
t.Fatal("an unknown encoding was accepted")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user