Summary
In pkg/cli/project_command.go:159, the GraphQL response field project["id"] is dereferenced with an unchecked single-value type assertion (string), while the same project map's "url" and "number" fields are checked with two-value , ok assertions a few lines below (lines 167, 172). A malformed or partial GitHub GraphQL response — id: null, missing field, or an unexpected scalar type — therefore panics the CLI process instead of returning a clean error.
Location
pkg/cli/project_command.go:159 — if err := linkProjectToRepo(ctx, project["id"].(string), config.Repo, config.Verbose); err != nil {
- Compare with sibling validated reads at:
pkg/cli/project_command.go:167 — projectURL, ok := project["url"].(string)
pkg/cli/project_command.go:172 — projectNumberFloat, ok := project["number"].(float64)
Severity
High for the affected code path (project-create CLI flow):
- The panic crashes
gh aw project create --repo <slug> with a runtime stack trace instead of "failed to get project ID from response".
createProject (line 289) does validate the GraphQL envelope (data → createProjectV2 → projectV2) but does not validate individual scalar fields, so id/url/number reach the caller as raw any.
- Trust boundary: the value originates from a remote GitHub API call. Defense-in-depth is warranted.
Suggested Fix
Apply the same defensive pattern already used for url / number, BEFORE the linkProjectToRepo call so the early-return path is consistent:
projectID, ok := project["id"].(string)
if !ok || projectID == "" {
return errors.New("failed to get project ID from response")
}
if config.Repo != "" {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Linking project to repository %s...", config.Repo)))
if err := linkProjectToRepo(ctx, projectID, config.Repo, config.Verbose); err != nil {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Warning: Failed to link project to repository: %v", err)))
} else {
fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("✓ Project linked to repository"))
}
}
Adjacent Risky Sites (lower priority — local-literal / sync-pool patterns are safe-by-convention; flagged for awareness, not action)
| File:Line |
Pattern |
Risk |
pkg/cli/jsonworkflow_to_markdown.go:448 |
return intervalType.(string) |
Lower — intervalType is parts["_interval"] populated internally; map is private. Add defensive , ok if refactored. |
pkg/cli/jsonworkflow_to_markdown.go:455 |
intervalToFuzzySchedule(intervalType.(string)) |
Same as above — duplicate site. |
pkg/workflow/safe_outputs_workflow_helpers.go:41 |
tool["inputSchema"].(map[string]any)["required"] = required |
Safe-by-construction (literal 4 lines above), but a local var would be more refactor-resistant. |
pkg/workflow/mcp_scripts_generator.go:61 |
inputSchema["properties"].(map[string]any) |
Same as above — literal-map safe. |
pkg/workflow/yaml.go:173, repository_features_validation.go:185/212, pkg/cli/workflows.go:357 |
sync.Map.Load / sync.Pool.Get casts |
Idiomatic Go — only one type ever stored. Documented pattern. No change recommended. |
pkg/linters/*/...:N (12 sites) |
pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) |
go/analysis framework idiom — guaranteed by framework. No change recommended. |
Validation
Estimated Effort
Small — ~6-line change, single file, mirrors an established local pattern.
Generated by Sergo — The Serena Go Expert (Run 18)
Strategy: reverify-plus-unchecked-type-assertion-audit
Generated by 🤖 Sergo - Serena Go Expert · opus47 25.4M · ◷
Summary
In
pkg/cli/project_command.go:159, the GraphQL response fieldproject["id"]is dereferenced with an unchecked single-value type assertion(string), while the sameprojectmap's"url"and"number"fields are checked with two-value, okassertions a few lines below (lines 167, 172). A malformed or partial GitHub GraphQL response —id: null, missing field, or an unexpected scalar type — therefore panics the CLI process instead of returning a clean error.Location
pkg/cli/project_command.go:159—if err := linkProjectToRepo(ctx, project["id"].(string), config.Repo, config.Verbose); err != nil {pkg/cli/project_command.go:167—projectURL, ok := project["url"].(string)pkg/cli/project_command.go:172—projectNumberFloat, ok := project["number"].(float64)Severity
High for the affected code path (project-create CLI flow):
gh aw project create --repo <slug>with a runtime stack trace instead of"failed to get project ID from response".createProject(line 289) does validate the GraphQL envelope (data→createProjectV2→projectV2) but does not validate individual scalar fields, soid/url/numberreach the caller as rawany.Suggested Fix
Apply the same defensive pattern already used for
url/number, BEFORE thelinkProjectToRepocall so the early-return path is consistent:Adjacent Risky Sites (lower priority — local-literal / sync-pool patterns are safe-by-convention; flagged for awareness, not action)
pkg/cli/jsonworkflow_to_markdown.go:448return intervalType.(string)intervalTypeisparts["_interval"]populated internally; map is private. Add defensive, okif refactored.pkg/cli/jsonworkflow_to_markdown.go:455intervalToFuzzySchedule(intervalType.(string))pkg/workflow/safe_outputs_workflow_helpers.go:41tool["inputSchema"].(map[string]any)["required"] = requiredpkg/workflow/mcp_scripts_generator.go:61inputSchema["properties"].(map[string]any)pkg/workflow/yaml.go:173,repository_features_validation.go:185/212,pkg/cli/workflows.go:357sync.Map.Load/sync.Pool.Getcastspkg/linters/*/...:N(12 sites)pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)go/analysisframework idiom — guaranteed by framework. No change recommended.Validation
gh aw project create --repo <slug>exits with the new error message instead of a panic when GraphQL returns a malformedid.TestCreateProject_MissingIDtable case using a stubbed GraphQL response.Estimated Effort
Small — ~6-line change, single file, mirrors an established local pattern.
Generated by Sergo — The Serena Go Expert (Run 18)
Strategy: reverify-plus-unchecked-type-assertion-audit