carlos / platform Public

activator: an adoption body is one object and nothing after it (Astra P3, final review minor 1)

Decoder.More is false before a trailing } or ] as well as at the end, and
hides a read error, so a record followed by either delimiter was written,
published and answered 204. The second Decode must now return io.EOF.
Tests: a trailing }, a trailing ], trailing garbage and a second object
are each a 400 with nothing written or published.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev 1ea86df4ca608228f04809593945348dfd9f7c4b parent 0ee78be
2 files changed, +13 −1
  • internal/activator/keyadoption.go +6 −1
  • internal/activator/keyadoption_test.go +7 −0
diff --git a/internal/activator/keyadoption.go b/internal/activator/keyadoption.go
index 2eb6d4b4e..cd0e2314c 100644
--- a/internal/activator/keyadoption.go
+++ b/internal/activator/keyadoption.go
@@ -20,6 +20,7 @@ import (
"crypto/subtle"
"encoding/json"
"errors"
+ "io"
"net/http"
"os"
"path/filepath"
@@ -93,7 +94,11 @@ func (b *ExecBackend) MountInstanceKeyAdoption(mux *http.ServeMux, rt registry.R
}
dec := json.NewDecoder(http.MaxBytesReader(w, r.Body, 1024))
dec.DisallowUnknownFields()
- if err := dec.Decode(&body); err != nil || dec.More() || !instancekey.ValidDigest(body.SHA256) ||
+ // One object and nothing after it: the second Decode must find
+ // the body's end. Decoder.More is not that test: it is false
+ // before a trailing } or ] as well, and hides a read error, so a
+ // record followed by either was a 204.
+ if err := dec.Decode(&body); err != nil || dec.Decode(new(json.RawMessage)) != io.EOF || !instancekey.ValidDigest(body.SHA256) ||
(body.Source != instancekey.SourcePlatform && body.Source != instancekey.SourceConfig) {
fail(w, http.StatusBadRequest)
return
diff --git a/internal/activator/keyadoption_test.go b/internal/activator/keyadoption_test.go
index 4d775129e..e4388ae98 100644
--- a/internal/activator/keyadoption_test.go
+++ b/internal/activator/keyadoption_test.go
@@ -67,6 +67,13 @@ func TestInstanceKeyAdoptionRecordsTheLaunchesWriter(t *testing.T) {
{"not a digest", tok, `{"sha256":"abc","source":"platform"}`, http.StatusBadRequest},
{"an unknown source", tok, adoptionBody(key, "own"), http.StatusBadRequest},
{"a session in the request", tok, `{"sha256":"` + instancekey.Digest(key) + `","source":"platform","session":9}`, http.StatusBadRequest},
+ // Astra's P3: the body is one object and nothing after it. A
+ // trailing } or ] passed Decoder.More; the second Decode must
+ // answer io.EOF.
+ {"a trailing }", tok, adoptionBody(key, "platform") + "}", http.StatusBadRequest},
+ {"a trailing ]", tok, adoptionBody(key, "platform") + "]", http.StatusBadRequest},
+ {"trailing garbage", tok, adoptionBody(key, "platform") + " xyz", http.StatusBadRequest},
+ {"a second object", tok, adoptionBody(key, "platform") + adoptionBody(key, "platform"), http.StatusBadRequest},
{"another key", tok, adoptionBody("some-other-key", "platform"), http.StatusConflict},
{"another source", tok, adoptionBody(key, "config"), http.StatusConflict},
} {