package agent import ( "context" "crypto/sha256" "errors" rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1" "github.com/rvbox/rvbox/internal/agentproto" "github.com/rvbox/rvbox/internal/client/spool" "github.com/rvbox/rvbox/internal/domain" ) var ErrInvalidScriptTransfer = errors.New("invalid script transfer") // ApplyScriptChunk validates one server script frame and appends it to the // durable client spool. The spool accepts only the next contiguous range or // an exact replay, so reconnects cannot create holes or duplicate bytes. func ApplyScriptChunk(ctx context.Context, store *spool.Store, session Session, chunk *rvboxv1.ScriptChunk, limits agentproto.Limits) (spool.ScriptStatus, error) { if store == nil || session.Generation == 0 || chunk == nil || chunk.GetOffset() > limits.MaxScriptBytes || uint64(len(chunk.GetData())) > limits.MaxRawChunkBytes || len(chunk.GetSha256()) != sha256.Size { return spool.ScriptStatus{}, ErrInvalidScriptTransfer } issue, err := domain.ParseUUIDv7(chunk.GetIssueUuid()) if err != nil || sha256.Sum256(chunk.GetData()) != bytesToDigest(chunk.GetSha256()) { return spool.ScriptStatus{}, ErrInvalidScriptTransfer } return store.AppendScriptChunk(ctx, issue, chunk.GetOffset(), chunk.GetData(), bytesToDigest(chunk.GetSha256())) } // ApplyScriptCommit marks a fully received script launch-eligible only after // the spool verifies the declared size and whole-body SHA-256. func ApplyScriptCommit(ctx context.Context, store *spool.Store, session Session, commit *rvboxv1.ScriptCommit, limits agentproto.Limits) (spool.ScriptStatus, error) { if store == nil || session.Generation == 0 || commit == nil || commit.GetSizeBytes() > limits.MaxScriptBytes || len(commit.GetSha256()) != sha256.Size { return spool.ScriptStatus{}, ErrInvalidScriptTransfer } issue, err := domain.ParseUUIDv7(commit.GetIssueUuid()) if err != nil { return spool.ScriptStatus{}, ErrInvalidScriptTransfer } return store.CommitScript(ctx, issue, spool.ScriptDescriptor{SizeBytes: commit.GetSizeBytes(), SHA256: bytesToDigest(commit.GetSha256())}) } func bytesToDigest(value []byte) [sha256.Size]byte { var digest [sha256.Size]byte copy(digest[:], value) return digest }