Bound ciphertext allocation and preserve unavailable inspection outcomes
4 files changed,
+25
−8
internal/home/compatibility_linux.go+5 −5internal/home/compatibility_linux_test.go+7 −1internal/operator/readonly_linux.go+3 −0internal/operator/readonly_linux_test.go+10 −2
CI passed — run details
diff --git a/internal/home/compatibility_linux.go b/internal/home/compatibility_linux.go| index 28136b6..469074e 100644 |
| --- a/internal/home/compatibility_linux.go |
| +++ b/internal/home/compatibility_linux.go |
| @@ -126,7 +126,7 @@ func inspectCompatibilityDatabase(ctx context.Context, expected operator.Readonl |
| return operator.ErrRefused |
| } |
| } |
| - if e := budget.scan(ctx, c, "SELECT operation_id,action,sealed_result FROM "+prefix+"_results", func(rows *sql.Rows) error { |
| + if e := budget.scan(ctx, c, "SELECT operation_id,action,CASE WHEN typeof(sealed_result)='blob' AND length(sealed_result)<=49180 THEN sealed_result END FROM "+prefix+"_results", func(rows *sql.Rows) error { |
| var id, action string |
| var ciphertext []byte |
| if rows.Scan(&id, &action, &ciphertext) != nil || !canonicalHash(id) || len(action) == 0 || len(action) > 64 { |
| @@ -137,7 +137,7 @@ func inspectCompatibilityDatabase(ctx context.Context, expected operator.Readonl |
| return e |
| } |
| if cfg.role == "credential" { |
| - return budget.scan(ctx, c, "SELECT sealed_address FROM signin_links", func(rows *sql.Rows) error { |
| + return budget.scan(ctx, c, "SELECT CASE WHEN typeof(sealed_address)='blob' AND length(sealed_address)<=4096 THEN sealed_address END FROM signin_links", func(rows *sql.Rows) error { |
| var ciphertext []byte |
| if rows.Scan(&ciphertext) != nil { |
| return operator.ErrRefused |
| @@ -145,7 +145,7 @@ func inspectCompatibilityDatabase(ctx context.Context, expected operator.Readonl |
| return budget.open(sealer, "signin.address", ciphertext, 4096, func(raw []byte) bool { return len(raw) > 0 && utf8.Valid(raw) }) |
| }) |
| } |
| - if e := budget.scan(ctx, c, "SELECT id,sqid,label,roster_epoch,sealed_name FROM teams", func(rows *sql.Rows) error { |
| + if e := budget.scan(ctx, c, "SELECT id,sqid,label,roster_epoch,CASE WHEN typeof(sealed_name)='blob' AND length(sealed_name)<=284 THEN sealed_name END FROM teams", func(rows *sql.Rows) error { |
| var id, epoch int64 |
| var sqid, label string |
| var ciphertext []byte |
| @@ -203,8 +203,8 @@ func inspectCompatibilityDatabase(ctx context.Context, expected operator.Readonl |
| limit int |
| path bool |
| }{ |
| - {"SELECT secret_hash,sealed_path FROM oficina_handoff_pending", "handoff.path.", 8192, true}, |
| - {"SELECT handle_hash,sealed_token FROM oficina_handoff_inbox", "handoff.token.", 16384, false}, |
| + {"SELECT secret_hash,CASE WHEN typeof(sealed_path)='blob' AND length(sealed_path)<=8192 THEN sealed_path END FROM oficina_handoff_pending", "handoff.path.", 8192, true}, |
| + {"SELECT handle_hash,CASE WHEN typeof(sealed_token)='blob' AND length(sealed_token)<=16384 THEN sealed_token END FROM oficina_handoff_inbox", "handoff.token.", 16384, false}, |
| } { |
| if e := budget.scan(ctx, c, item.query, func(rows *sql.Rows) error { |
| var hash string |
diff --git a/internal/home/compatibility_linux_test.go b/internal/home/compatibility_linux_test.go| index 6529d8b..51b011a 100644 |
| --- a/internal/home/compatibility_linux_test.go |
| +++ b/internal/home/compatibility_linux_test.go |
| @@ -178,13 +178,19 @@ func TestCompatibilityDatabaseSubprocess(t *testing.T) { |
| return |
| } |
| for _, role := range []string{"credential", "directory"} { |
| - for _, mode := range []string{"valid", "wrong-key-enrolled", "operator-ciphertext", "path-ciphertext", "missing-proof", "missing-key", "wrong-key", "wrong-state", "wrong-witness", "schema", "ledger", "ciphertext", "replication", "foreign-key", "empty-state"} { |
| + for _, mode := range []string{"valid", "oversized-ciphertext", "wrong-key-enrolled", "operator-ciphertext", "path-ciphertext", "missing-proof", "missing-key", "wrong-key", "wrong-state", "wrong-witness", "schema", "ledger", "ciphertext", "replication", "foreign-key", "empty-state"} { |
| t.Run(role+"/"+mode, func(t *testing.T) { |
| cfg := compatibilityTestConfig(role) |
| d := compatibilityFixture(t, role) |
| seedCompatibilityData(t, d, cfg) |
| q := "" |
| switch mode { |
| + case "oversized-ciphertext": |
| + if role == "credential" { |
| + q = "UPDATE signin_links SET sealed_address=zeroblob(65536)" |
| + } else { |
| + q = "UPDATE oficina_handoff_inbox SET sealed_token=zeroblob(65536)" |
| + } |
| case "operator-ciphertext": |
| q = "UPDATE " + role + "_operator_results SET sealed_result=zeroblob(40)" |
| case "path-ciphertext": |
diff --git a/internal/operator/readonly_linux.go b/internal/operator/readonly_linux.go| index 3ef8066..8ade850 100644 |
| --- a/internal/operator/readonly_linux.go |
| +++ b/internal/operator/readonly_linux.go |
| @@ -99,6 +99,9 @@ func inspectReadonly(ctx context.Context, expected ReadonlyExpectation, verify f |
| return ErrUnavailable |
| } |
| if e != nil { |
| + if e == ErrUnavailable { |
| + return ErrUnavailable |
| + } |
| return ErrRefused |
| } |
| if conn.Close() != nil || d.Close() != nil { |
diff --git a/internal/operator/readonly_linux_test.go b/internal/operator/readonly_linux_test.go| index e7f9e19..de0a190 100644 |
| --- a/internal/operator/readonly_linux_test.go |
| +++ b/internal/operator/readonly_linux_test.go |
| @@ -58,6 +58,9 @@ func TestReadonlySubprocess(t *testing.T) { |
| } |
| }() |
| inspect := func(ctx context.Context, c *sql.Conn) error { |
| + if input.Mode == "unavailable" { |
| + return ErrUnavailable |
| + } |
| if input.Mode == "spill" { |
| var e error |
| extra, e = os.Create(input.Expected.Path + ".spill") |
| @@ -78,7 +81,9 @@ func TestReadonlySubprocess(t *testing.T) { |
| } |
| return nil |
| } |
| + lastStage := "before-inspection" |
| hook := func(stage string) { |
| + lastStage = stage |
| if input.Mode == "replace-restore" { |
| if stage == "before-driver" { |
| if os.Rename(input.Expected.Path, input.Expected.Path+".saved") != nil || os.Rename(input.Expected.Path+".other", input.Expected.Path) != nil { |
| @@ -101,16 +106,19 @@ func TestReadonlySubprocess(t *testing.T) { |
| } |
| } |
| err := inspectReadonly(ctx, input.Expected, proof, inspect, hook) |
| + if input.Mode == "unavailable" && err != ErrUnavailable { |
| + t.Fatalf("lost unavailable outcome: %v", err) |
| + } |
| if input.Mode == "valid" { |
| if err != nil { |
| - t.Fatal(err) |
| + t.Fatalf("inspection refused after %s: %v", lastStage, err) |
| } |
| } else if err == nil { |
| t.Fatal("unsafe success", input.Mode) |
| } |
| return |
| } |
| - for _, mode := range []string{"valid", "proof-revoked", "replace-restore", "missing-proof", "wrong-hash", "wrong-inode", "expired", "unbounded", "wal", "shm", "journal", "spill", "write", "change-after-read"} { |
| + for _, mode := range []string{"valid", "unavailable", "proof-revoked", "replace-restore", "missing-proof", "wrong-hash", "wrong-inode", "expired", "unbounded", "wal", "shm", "journal", "spill", "write", "change-after-read"} { |
| t.Run(mode, func(t *testing.T) { |
| path := filepath.Join(t.TempDir(), "test.db") |
| seedOpenDB(t, path) |