diff --git a/pkg/parser/schema_location_test.go b/pkg/parser/schema_location_test.go index 0aea58410b4..6a3076fd374 100644 --- a/pkg/parser/schema_location_test.go +++ b/pkg/parser/schema_location_test.go @@ -250,6 +250,81 @@ func TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_AdditionalProperti wantErr: true, errContains: "/test/workflow.md:2:1:", }, + { + name: "workflow_call input typo now fails with additional property error", + frontmatter: map[string]any{ + "on": map[string]any{ + "workflow_call": map[string]any{ + "inputs": map[string]any{ + "payload": map[string]any{ + "type": "string", + "requird": true, + }, + }, + }, + }, + }, + filePath: "/test/workflow.md", + wantErr: true, + errContains: "requird", + }, + { + name: "workflow_call secret typo now fails with additional property error", + frontmatter: map[string]any{ + "on": map[string]any{ + "workflow_call": map[string]any{ + "secrets": map[string]any{ + "token": map[string]any{ + "requird": true, + }, + }, + }, + }, + }, + filePath: "/test/workflow.md", + wantErr: true, + errContains: "requird", + }, + { + name: "dispatch_repository input typo now fails with additional property error", + frontmatter: map[string]any{ + "on": "workflow_dispatch", + "safe-outputs": map[string]any{ + "dispatch_repository": map[string]any{ + "relay": map[string]any{ + "event_type": "dispatch", + "repository": "github/gh-aw", + "inputs": map[string]any{ + "payload": map[string]any{ + "type": "string", + "requird": true, + }, + }, + }, + }, + }, + }, + filePath: "/test/workflow.md", + wantErr: true, + errContains: "requird", + }, + { + name: "valid workflow_call input still compiles", + frontmatter: map[string]any{ + "on": map[string]any{ + "workflow_call": map[string]any{ + "inputs": map[string]any{ + "payload": map[string]any{ + "type": "string", + "required": true, + }, + }, + }, + }, + }, + filePath: "/test/workflow.md", + wantErr: false, + }, } for _, tt := range tests { diff --git a/pkg/parser/schema_test.go b/pkg/parser/schema_test.go index 9d3072713ac..6d9a67d58d2 100644 --- a/pkg/parser/schema_test.go +++ b/pkg/parser/schema_test.go @@ -540,6 +540,83 @@ func TestMainWorkflowSchema_WorkflowDispatchNumberTypeDocumentation(t *testing.T } } +func TestMainWorkflowSchema_WorkflowCallAndDispatchInputDefsDisallowUnknownKeys(t *testing.T) { + t.Parallel() + + schemaContent, err := os.ReadFile("schemas/main_workflow_schema.json") + if err != nil { + t.Fatalf("failed to read schema: %v", err) + } + + var schema map[string]any + if err := json.Unmarshal(schemaContent, &schema); err != nil { + t.Fatalf("failed to parse schema json: %v", err) + } + + getMapAtPath := func(t *testing.T, root map[string]any, path ...any) map[string]any { + t.Helper() + var cur any = root + for _, p := range path { + switch step := p.(type) { + case string: + m, ok := cur.(map[string]any) + if !ok { + t.Fatalf("expected map before key %q", step) + } + next, ok := m[step] + if !ok { + t.Fatalf("missing key %q in path", step) + } + cur = next + case int: + arr, ok := cur.([]any) + if !ok { + t.Fatalf("expected array before index %d", step) + } + if step < 0 || step >= len(arr) { + t.Fatalf("index %d out of range in path", step) + } + cur = arr[step] + default: + t.Fatalf("unsupported path step type %T", p) + } + } + + out, ok := cur.(map[string]any) + if !ok { + t.Fatalf("expected map at target path, got %T", cur) + } + return out + } + + paths := []struct { + name string + path []any + }{ + { + name: "on.workflow_call.inputs.", + path: []any{"properties", "on", "oneOf", 1, "properties", "workflow_call", "oneOf", 1, "properties", "inputs", "additionalProperties"}, + }, + { + name: "on.workflow_call.secrets.", + path: []any{"properties", "on", "oneOf", 1, "properties", "workflow_call", "oneOf", 1, "properties", "secrets", "additionalProperties"}, + }, + { + name: "safe-outputs.dispatch_repository..inputs.", + path: []any{"properties", "safe-outputs", "properties", "dispatch_repository", "additionalProperties", "properties", "inputs", "additionalProperties"}, + }, + } + + for _, tc := range paths { + t.Run(tc.name, func(t *testing.T) { + subschema := getMapAtPath(t, schema, tc.path...) + if subschema["additionalProperties"] != false { + t.Fatalf("%s should set additionalProperties to false", tc.name) + } + }) + } +} + func TestMainWorkflowSchema_CreatePullRequestAllowedBaseBranches(t *testing.T) { t.Parallel() diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index d38819964db..c742b6c31f4 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -1637,7 +1637,8 @@ "default": { "description": "Default value for the input parameter" } - } + }, + "additionalProperties": false } }, "secrets": { @@ -1654,7 +1655,8 @@ "type": "boolean", "description": "Whether the secret is required" } - } + }, + "additionalProperties": false } } } @@ -8411,7 +8413,8 @@ "type": "string" } } - } + }, + "additionalProperties": false } }, "max": {