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>
2 files changed,
+13
−1
internal/activator/keyadoption.go+6 −1internal/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}, |
| } { |