Repository navigation
Description
Found while implementing Cookit-labs/Intent-Agent#2, which aligns the agent service's proposal schema with this backend's DTOs.
Problem
CreateProposalRequest in internal/handlers/types.go carries validation tags on every field:
type CreateProposalRequest struct {
AgentID string `json:"agent_id" validate:"required"`
StrategyType string `json:"strategy_type" validate:"required"`
ProjectedSlippage float64 `json:"projected_slippage" validate:"required,gte=0"`
ProjectedExecutionQuality float64 `json:"projected_execution_quality" validate:"required,gte=0,lte=1"`
ProposedAmount string `json:"proposed_amount" validate:"required"`
ExecutionPath string `json:"execution_path" validate:"required"`
}Nothing enforces them. There is no validator library in go.mod — no go-playground/validator or equivalent — and no code reads the tags. CreateProposal in internal/handlers/proposals.go decodes and persists:
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
respondError(w, http.StatusBadRequest, "invalid request body")
return
}The same applies to CreateIntentRequest, ValidateExecutionRequest, and SettleExecutionRequest.
Impact
A proposal with an empty agent_id, zero projected_slippage, and an empty execution_path is accepted and written today. Concretely:
projected_execution_qualityoutside 0–1 corrupts scoring ininternal/scoring/engine.go, since the engine assumes a bounded input- zero
projected_slippagemakes a proposal look optimal, so a malformed or malicious bid outranks every honest one - an empty
execution_pathgives a winning proposal no venue to execute against SettleExecutionRequest.ActualSlippageandExecutionFeeUSDCfeed reputation and fee accounting with no bounds check
The tags being present is arguably worse than their absence: they read as enforced, so a caller reviewing the DTO would reasonably assume the boundary is guarded.
Options
- Add
go-playground/validator/v10and run it in each handler after decode. Matches the tags already written; smallest diff. - Validate explicitly in each handler and delete the tags. No dependency, more code, no chance of the tags drifting from reality again.
- Delete the tags and document that validation is the caller's job. Only honest if the callers are trusted, which they are not once the marketplace is open.
Option 1 unless the team objects to the dependency.
Note
Cookit-labs/Intent-Agent#2 added matching range validation on the Python side, so proposals from that service are bounded before they are sent. That is defence in depth, not a substitute — it only covers one caller, and a caller validating its own requests is not a boundary.
Activity
0xDeon commentedon Sep 8, 2026
Fixed in #27 (merged).
Validate tags are now enforced at all seven decode sites, with every failure reported at once and fields named as the caller sent them. Slippage and quality moved from
requiredto range checks, sincerequiredrejects the zero value and zero projected slippage is a legitimate claim.