Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions protocol/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,12 @@ func (r ProviderUpstreamClientRequirement) Validate() error {
errs = append(errs, fmt.Errorf("%s is required", field.name))
}
}
if strings.TrimSpace(r.Version) != r.Version || strings.ContainsAny(r.Version, "\t\r\n\x00") {
errs = append(errs, errors.New("version is required"))
}
if strings.TrimSpace(r.UpstreamClientName) != r.UpstreamClientName || strings.ContainsAny(r.UpstreamClientName, "\t\r\n\x00") {
errs = append(errs, errors.New("upstream_client_name is required"))
}
if r.ProtocolVersion != Version {
errs = append(errs, fmt.Errorf("protocol_version must be %q", Version))
}
Expand All @@ -1048,6 +1054,21 @@ func (r ProviderUpstreamClientRequirement) Validate() error {
if err := r.ImagePolicy.Validate(); err != nil {
errs = append(errs, err)
}
for i, value := range r.VersionProbeCommand {
if strings.TrimSpace(value) == "" || strings.ContainsAny(value, "\t\r\n\x00") {
errs = append(errs, fmt.Errorf("version_probe_command[%d] is required", i))
}
}
for i, value := range r.RequiredEvidence {
if strings.TrimSpace(value) == "" || strings.ContainsAny(value, "\t\r\n\x00") {
errs = append(errs, fmt.Errorf("required_evidence[%d] is required", i))
}
}
for i, value := range r.Notes {
if strings.TrimSpace(value) == "" || strings.ContainsAny(value, "\t\r\n\x00") {
errs = append(errs, fmt.Errorf("notes[%d] is required", i))
}
}
return errors.Join(errs...)
}

Expand Down
42 changes: 42 additions & 0 deletions protocol/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,48 @@ func TestProviderUpstreamImagePolicyRequiresRecommendedImageUnlessOperatorSuppli
}
}

func TestProviderUpstreamClientRequirementRejectsControlWhitespaceLists(t *testing.T) {
req := protocol.ProviderUpstreamClientRequirement{
ProtocolVersion: protocol.Version,
PluginID: "workflow-plugin-crypto",
ProviderID: "ethereum-full-node",
ContractID: "ethereum-full-node.v1",
Version: "v1.0.0",
RuntimeProfileID: "sandboxed-container-runtime",
ConformanceProfile: "upstream-client-v1",
DefaultConformance: protocol.UpstreamClientConformanceShapeOnly,
RealClientConformance: protocol.UpstreamClientConformanceRealClient,
UpstreamClientName: "geth",
VersionProbeCommand: []string{"geth version"},
ImagePolicy: protocol.ProviderUpstreamImagePolicy{
DigestPinnedImageRequired: true,
RecommendedImageRef: "ethereum/client-go@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
RequiredEvidence: []string{"artifact://provider/evidence"},
Notes: []string{"operator may provide a digest-pinned image"},
}
if err := req.Validate(); err != nil {
t.Fatalf("requirement invalid: %v", err)
}

req.VersionProbeCommand = []string{"geth\nversion"}
if err := req.Validate(); err == nil || !strings.Contains(err.Error(), "version_probe_command") {
t.Fatalf("expected version_probe_command error, got %v", err)
}
req.VersionProbeCommand = []string{"geth version"}

req.RequiredEvidence = []string{""}
if err := req.Validate(); err == nil || !strings.Contains(err.Error(), "required_evidence") {
t.Fatalf("expected required_evidence error, got %v", err)
}
req.RequiredEvidence = []string{"artifact://provider/evidence"}

req.Notes = []string{" "}
if err := req.Validate(); err == nil || !strings.Contains(err.Error(), "notes") {
t.Fatalf("expected notes error, got %v", err)
}
}

func TestProviderRuntimeProfileRejectsReusableResidueWithoutWorkspace(t *testing.T) {
contract := validBatchProviderContract()
profile := &contract.RuntimeContract.Profiles[0]
Expand Down
Loading