## Code Analysis ### Deserialization errors are converted into a process panic `server/auth/database.go:271-295` ```go // dbInit handle the global database initialization. Panics if an error occurs, since it points to something going // terribly wrong. func dbInit(dEnv *env.DoltEnv, cfg Config) { globalDatabase = newEmptyDatabase() globalLock = &sync.RWMutex{} if dEnv != nil { if _, ok := dEnv.FS.(*filesys.InMemFS); !ok { if cfg != nil && len(cfg.AuthFilePath()) > 0 { authFileName = cfg.AuthFilePath() } fileSystem = dEnv.FS authData, err := fileSystem.ReadFile(authFileName) if os.IsNotExist(err) { dbInitDefault() if err = dbInitCreateAuthDirectory(authFileName); err != nil { panic(err) } if err = fileSystem.WriteFile(authFileName, globalDatabase.serialize(), 0644); err != nil { panic(err) } } else if err != nil { panic(err) } else if err = globalDatabase.deserialize(authData); err != nil { panic(err) } } } } ``` The excerpt preserves the production control flow around the relevant branch: the startup loader reads the configured authorization file and sends any deserialization error to `panic` rather than returning a controlled startup error. The omitted lines are unrelated setup and file-creation details from the same function. ### Truncated data reaches unchecked reader operations `server/auth/serialization.go:66-85` ```go func (db *Database) deserialize(data []byte) error { if len(data) < 4 { return errors.New("invalid auth database format") } reader := utils.NewReader(data) version := reader.Uint32() var err error switch version { case 0: err = db.deserializeV0(reader) case 1: err = db.deserializeV1(reader) case 2: err = db.deserializeV2(reader) default: return errors.Errorf("Authorization database format %d is not supported, please upgrade Doltgres", version) } if err != nil { return err } db.removeInvalidRoleReferences() // Advance the role ID counter past every persisted role. Without this, IDs minted after loading serialized // state collide with existing roles, which SetRole then silently replaces. } ``` `server/auth/default_privileges.go:377-407` ```go func (dp *DefaultPrivileges) deserialize(version uint32, reader *utils.Reader) { dp.Data = make(map[DefaultPrivilegeKey]DefaultPrivilegeValue) switch version { case 0: case 1: case 2: dataCount := reader.Uint64() for i := uint64(0); i < dataCount; i++ { dpv := DefaultPrivilegeValue{ Grantees: make(map[RoleID]DefaultPrivilegeGranteeValue), } dpv.Key.OwnerRole = RoleID(reader.Uint64()) dpv.Key.Schema = reader.String() dpv.Key.ObjectType = PrivilegeObject(reader.Uint8()) granteeCount := reader.Uint64() for j := uint64(0); j < granteeCount; j++ { granteeValue := DefaultPrivilegeGranteeValue{ Grantee: RoleID(reader.Uint64()), Privileges: make(map[Privilege]map[GrantedPrivilege]bool), } privCount := reader.Uint64() for k := uint64(0); k < privCount; k++ { priv := Privilege(reader.String()) grantedCount := reader.Uint32() grantedMap := make(map[GrantedPrivilege]bool) for l := uint32(0); l < grantedCount; l++ { gp := GrantedPrivilege{ Privilege: priv, GrantedBy: RoleID(reader.Uint64()), } grantedMap[gp] = reader.Bool() } granteeValue.Privileges[priv] = grantedMap } dpv.Grantees[granteeValue.Grantee] = granteeValue } dp.Data[dpv.Key] = dpv } default: panic("unexpected version in SequencePrivileges") } } ``` `utils/reader.go:69-90` ```go func (reader *Reader) Uint32() uint32 { reader.offset += 4 return binary.BigEndian.Uint32(reader.buf[reader.offset-4:]) } func (reader *Reader) Uint64() uint64 { reader.offset += 8 return binary.BigEndian.Uint64(reader.buf[reader.offset-8:]) } ``` The v2 default-privilege decoder reads counts and fields from the shared byte slice without returning truncation errors. For a long-enough-to-pass-header but incomplete payload, these reads can panic on an out-of-range slice; for the three-byte payload, `deserialize` first returns the explicit invalid-format error, which `dbInit` then panics on. ### Observed execution The runner wrote a three-byte authorization database and captured the startup result: ```text $ printf "\\x00\\x00\\x00" > .build-agent/doltgres-data/auth.db # Fixture readback: 3 bytes time="2026-10-09T20:25:14Z" level=info msg="startup integrity check passed in 4.522478ms (0 checked, 1 previously passed)" time="2026-10-09T20:25:14Z" level=info msg="Server ready. Accepting connections." ``` The browser probe was `playwright-cli goto http://localhost:5432` and returned `net::ERR_EMPTY_RESPONSE`; that endpoint is a PostgreSQL wire-protocol service rather than HTTP. The recorded startup log did not show a controlled malformed-auth rejection. A second root-file restart attempt was inconclusive because the earlier process remained active, so the runtime observation is not treated as proof of the panic path; the source excerpts independently establish the uncontrolled error handling. ### Result The source-backed finding is supported: malformed authorization bytes can be converted into an uncontrolled startup panic, and incomplete longer payloads also reach unchecked deserialization reads. The captured three-byte run additionally shows the configured fixture and a startup log that reported readiness instead of a clear malformed-file rejection. ### Test context The runner restored both authorization database fixtures after the attempt; no mocks or bypasses were applied.