Skip to content
Merged
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
22 changes: 12 additions & 10 deletions pkg/workflow/repository_features_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ import (

var repositoryFeaturesLog = newValidationLogger("repository_features")

// checkRepositoryHasDiscussionsQuery is a hardcoded static GraphQL query template used to check
// if discussions are enabled for a repository. Declared as a named constant to make clear
// it is not user-controlled input (CWE-89 / workflow-graphql-static-concat).
const checkRepositoryHasDiscussionsQuery = `query($owner: String!, $name: String!) {
repository(owner: $owner, name: $name) {
hasDiscussionsEnabled
}
}`

// RepositoryFeatures holds cached information about repository capabilities
type RepositoryFeatures struct {
HasDiscussions bool
Expand Down Expand Up @@ -252,22 +261,15 @@ func checkRepositoryHasDiscussions(repo string, verbose bool) (bool, error) {

// checkRepositoryHasDiscussionsUncached checks if a repository has discussions enabled (no caching)
func checkRepositoryHasDiscussionsUncached(repo string) (bool, error) {
// Use GitHub GraphQL API to check if discussions are enabled
// The hasDiscussionsEnabled field is the canonical way to check this
query := `query($owner: String!, $name: String!) {
repository(owner: $owner, name: $name) {
hasDiscussionsEnabled
}
}`

// Split repo into owner and name
parts := strings.SplitN(repo, "/", 2)
if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
return false, fmt.Errorf("invalid repository format: %s. Expected format: owner/repo. Example: github/gh-aw", repo)
}
owner, name := parts[0], parts[1]

// Execute GraphQL query using gh CLI
// Execute GraphQL query using gh CLI.
// checkRepositoryHasDiscussionsQuery is a package-level constant — not user-controlled.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/improve-codebase-architecture] This call-site comment repeats what the package-level const doc comment (lines 57–59) already explains — consider removing it to keep the call site clean.

💡 Detail

The const declaration already documents:

Declared as a named constant to make clear it is not user-controlled input (CWE-89 / workflow-graphql-static-concat).

The name checkRepositoryHasDiscussionsQuery itself carries enough signal at the call site. Removing the inline comment reduces redundancy without losing any safety intent.

type GraphQLResponse struct {
Data struct {
Repository struct {
Expand All @@ -276,7 +278,7 @@ func checkRepositoryHasDiscussionsUncached(repo string) (bool, error) {
} `json:"data"`
}

stdOut, _, err := gh.Exec("api", "graphql", "-f", "query="+query,
stdOut, _, err := gh.Exec("api", "graphql", "-f", "query="+checkRepositoryHasDiscussionsQuery,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/zoom-out] pkg/cli/project_command.go has 7 similar "query="+<var> call sites — some of which interpolate user-supplied input directly into the query string via fmt.Sprintf. These may trigger the same scanner rule (or a stricter variant) and are worth a follow-up sweep.

💡 Affected lines in project_command.go
Line Pattern
235–240 fmt.Sprintf(query { organization(login: "%s") ... }, escapeGraphQLString(owner)) then "query="+query
237–240 same for user login
275 "query="+query after dynamic Sprintf build
306, 361, 385 mutation strings with "query="+mutation
664, 671 project info/fields queries

Even with escapeGraphQLString, the approach differs from this PR's pattern (a pure static const). Tracking these as potential follow-up items would help close out the broader CWE-89 coverage.

"-f", "owner="+owner, "-f", "name="+name)
Comment on lines +281 to 282
if err != nil {
return false, fmt.Errorf("failed to query discussions status: %w", err)
Expand Down
Loading