fix: carry immutable hash in command dispatch
This commit is contained in:
@@ -188,7 +188,7 @@ func validateClientHello(hello *rvboxv1.ClientHello) error {
|
||||
}
|
||||
|
||||
func validateCommandDispatch(dispatch *rvboxv1.CommandDispatch, limits Limits, peerPlatform rvboxv1.Platform) error {
|
||||
if dispatch == nil || dispatch.CommandRevision == 0 || dispatch.TargetSessionGeneration == 0 || dispatch.IssueTime == nil || dispatch.IssueTime.CheckValid() != nil {
|
||||
if dispatch == nil || dispatch.CommandRevision == 0 || dispatch.TargetSessionGeneration == 0 || dispatch.IssueTime == nil || dispatch.IssueTime.CheckValid() != nil || len(dispatch.ImmutableRequestSha256) != sha256.Size {
|
||||
return fmt.Errorf("%w: invalid command dispatch", ErrInvalidEnvelope)
|
||||
}
|
||||
if err := validateIssueUUID(dispatch.IssueUuid); err != nil {
|
||||
|
||||
@@ -2,6 +2,7 @@ package agentproto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -140,7 +141,7 @@ func TestWindowsEnvironmentCaseCollision_BH_LAUNCH_01(t *testing.T) {
|
||||
func TestCommandDispatchUUIDRevisionAndTimestamp_BH_IDEM_01(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dispatch := &rvboxv1.CommandDispatch{IssueUuid: testIssueUUID, CommandRevision: 1, TargetSessionGeneration: 2, IssueTime: fixedTimestamp(), Spec: validCommandSpec()}
|
||||
dispatch := &rvboxv1.CommandDispatch{IssueUuid: testIssueUUID, CommandRevision: 1, TargetSessionGeneration: 2, IssueTime: fixedTimestamp(), Spec: validCommandSpec(), ImmutableRequestSha256: make([]byte, sha256.Size)}
|
||||
envelope := &rvboxv1.AgentEnvelope{SessionId: "session", SessionGeneration: 2, Payload: &rvboxv1.AgentEnvelope_CommandDispatch{CommandDispatch: dispatch}}
|
||||
if err := ValidateEnvelope(envelope, DefaultLimits(), rvboxv1.Platform_PLATFORM_LINUX); err != nil {
|
||||
t.Fatalf("valid dispatch: %v", err)
|
||||
@@ -149,6 +150,7 @@ func TestCommandDispatchUUIDRevisionAndTimestamp_BH_IDEM_01(t *testing.T) {
|
||||
func(value *rvboxv1.CommandDispatch) { value.IssueUuid = "not-a-uuid" },
|
||||
func(value *rvboxv1.CommandDispatch) { value.CommandRevision = 0 },
|
||||
func(value *rvboxv1.CommandDispatch) { value.TargetSessionGeneration = 0 },
|
||||
func(value *rvboxv1.CommandDispatch) { value.ImmutableRequestSha256 = nil },
|
||||
func(value *rvboxv1.CommandDispatch) {
|
||||
value.IssueTime = timestamppb.New(time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC))
|
||||
},
|
||||
|
||||
@@ -257,7 +257,7 @@ func (server *AgentServer) enqueueNextDispatch(ctx context.Context, queue *Write
|
||||
}
|
||||
dispatch := &rvboxv1.CommandDispatch{
|
||||
IssueUuid: candidate.IssueUUID.String(), CommandRevision: candidate.Revision, TargetSessionGeneration: generation,
|
||||
IssueTime: timestamppb.New(candidate.IssueTime), Spec: spec,
|
||||
IssueTime: timestamppb.New(candidate.IssueTime), Spec: spec, ImmutableRequestSha256: candidate.ImmutableSHA256[:],
|
||||
}
|
||||
if candidate.QueueExpiryTime != nil {
|
||||
dispatch.QueueExpiryTime = timestamppb.New(*candidate.QueueExpiryTime)
|
||||
|
||||
@@ -23,6 +23,7 @@ type DispatchCandidate struct {
|
||||
Revision uint64
|
||||
IssueTime time.Time
|
||||
QueueExpiryTime *time.Time
|
||||
ImmutableSHA256 [32]byte
|
||||
ExecutionSpec []byte
|
||||
}
|
||||
|
||||
@@ -51,13 +52,13 @@ func (store *Store) ClaimNextDispatch(ctx context.Context, clientID string, gene
|
||||
WHERE client_id = ? AND lifecycle = 1 AND queue_expiry_time IS NOT NULL AND queue_expiry_time <= ?`, now.UTC().UnixNano(), clientID, now.UTC().UnixNano()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var encodedIssue, stored []byte
|
||||
var encodedIssue, stored, immutableHash []byte
|
||||
var revision uint64
|
||||
var issueTime int64
|
||||
var expiry sql.NullInt64
|
||||
var rawBytes uint64
|
||||
err = tx.QueryRowContext(ctx, `SELECT issue_uuid, revision, issue_time, queue_expiry_time, execution_spec, execution_spec_raw_bytes
|
||||
FROM commands WHERE client_id = ? AND lifecycle = 1 ORDER BY issue_time, issue_uuid LIMIT 1`, clientID).Scan(&encodedIssue, &revision, &issueTime, &expiry, &stored, &rawBytes)
|
||||
err = tx.QueryRowContext(ctx, `SELECT issue_uuid, revision, issue_time, queue_expiry_time, immutable_request_sha256, execution_spec, execution_spec_raw_bytes
|
||||
FROM commands WHERE client_id = ? AND lifecycle = 1 ORDER BY issue_time, issue_uuid LIMIT 1`, clientID).Scan(&encodedIssue, &revision, &issueTime, &expiry, &immutableHash, &stored, &rawBytes)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
@@ -67,11 +68,13 @@ FROM commands WHERE client_id = ? AND lifecycle = 1 ORDER BY issue_time, issue_u
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(encodedIssue) != 16 {
|
||||
if len(encodedIssue) != 16 || len(immutableHash) != 32 {
|
||||
return nil, ErrInvalidSegmentRecord
|
||||
}
|
||||
var issue domain.UUID
|
||||
copy(issue[:], encodedIssue)
|
||||
var immutable [32]byte
|
||||
copy(immutable[:], immutableHash)
|
||||
spec, err := decompressCommandSpec(stored, rawBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -90,7 +93,7 @@ FROM commands WHERE client_id = ? AND lifecycle = 1 ORDER BY issue_time, issue_u
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
candidate := &DispatchCandidate{IssueUUID: issue, Revision: revision, IssueTime: time.Unix(0, issueTime).UTC(), ExecutionSpec: spec}
|
||||
candidate := &DispatchCandidate{IssueUUID: issue, Revision: revision, IssueTime: time.Unix(0, issueTime).UTC(), ImmutableSHA256: immutable, ExecutionSpec: spec}
|
||||
if expiry.Valid {
|
||||
value := time.Unix(0, expiry.Int64).UTC()
|
||||
candidate.QueueExpiryTime = &value
|
||||
|
||||
Reference in New Issue
Block a user