Skip to content

[code-scanning-fix] Fix workflow-graphql-id-unescaped: eliminate string interpolation in GraphQL mutations via variables - #40757

Merged
pelikhan merged 2 commits into
mainfrom
fix/graphql-id-unescaped-627-628-d08e45c37cbf4f35
Jun 22, 2026
Merged

[code-scanning-fix] Fix workflow-graphql-id-unescaped: eliminate string interpolation in GraphQL mutations via variables#40757
pelikhan merged 2 commits into
mainfrom
fix/graphql-id-unescaped-627-628-d08e45c37cbf4f35

Conversation

@github-actions

@github-actionsgithub-actionsBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Eliminates GraphQL string interpolation vulnerabilities (CWE-89 / workflow-graphql-id-unescaped) in pkg/cli/project_command.go by converting all three gh api graphql call sites from fmt.Sprintf-based query construction to fully parameterized GraphQL variables.

Changes

pkg/cli/project_command.go — modified (high impact, non-breaking)

Three call sites converted from inline string interpolation to GraphQL variables passed via -f flags:

Call siteBeforeAfter
createProjectV2 mutationfmt.Sprintf with escapeGraphQLString(title)mutation($ownerId: ID!, $title: String!) + -f ownerId=... -f title=...
repository(...) ID queryfmt.Sprintf with escapeGraphQLString on both fieldsquery($owner: String!, $name: String!) + -f owner=... -f name=...
linkProjectV2ToRepository mutationfmt.Sprintf with raw ID interpolationmutation($projectId: ID!, $repositoryId: ID!) + -f projectId=... -f repositoryId=...

Commit 3c4b872ac applied escapeGraphQLString() to un-escaped node ID fields as a stopgap. Commit c969477ed replaces the entire interpolation approach with parameterization, eliminating the need for escapeGraphQLString() at these call sites.

Security

  • Fixes:workflow-graphql-id-unescaped code scanning alerts (CWE-89)
  • Root cause removed: query structure is now static; no caller-supplied value can alter it regardless of content

Testing notes

No new tests added. The parameterized calls are functionally equivalent to the previous interpolated calls for all well-formed inputs.

Checklist

  • Non-breaking change (no API or CLI surface change)
  • Security fix (eliminates injection-class vulnerability)
  • Single file modified (pkg/cli/project_command.go)
  • No new dependencies introduced

Generated by PR Description Updater for issue #40757 · 56.1 AIC · ⌖ 6.91 AIC · ⊞ 4.5K ·

Wrap ownerId, projectId, and repositoryId with escapeGraphQLString() in
the createProjectV2 and linkProjectV2ToRepository GraphQL mutations.
Previously, title was consistently escaped but the node ID fields were
not, creating a defense-in-depth gap. While GitHub API node IDs are
opaque and currently safe, consistently applying escapeGraphQLString()
ensures that if the values ever contain special characters the queries
cannot be altered.
Fixes code scanning alerts #627 and #628.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

This comment has been minimized.

@pelikhan
pelikhan marked this pull request as ready for review June 22, 2026 14:14
CopilotAI review requested due to automatic review settings June 22, 2026 14:14

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.

This PR addresses code-scanning warnings for potential GraphQL injection by ensuring GitHub node IDs interpolated into GraphQL mutations are escaped consistently.

Changes:

  • Escape ownerId when building the createProjectV2 GraphQL mutation.
  • Escape projectId and repoId when building the linkProjectV2ToRepository GraphQL mutation.
Show a summary per file
FileDescription
pkg/cli/project_command.goEscapes node ID inputs embedded into GraphQL mutation strings to close a defense-in-depth injection gap.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 1/1 changed files
  • Comments generated: 2

Comment threadpkg/cli/project_command.go Outdated
Comment threadpkg/cli/project_command.go Outdated
}
}
}`, projectId, repoId)
}`, escapeGraphQLString(projectId), escapeGraphQLString(repoId))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Done in the latest commit. Both createProject and linkProjectToRepo (including its repo-ID query) now use static GraphQL query strings with variables passed as separate -f flags to gh api graphql. This eliminates all string interpolation from the query bodies, removing the need for manual escaping in these mutations entirely.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@github-actions

Copy link
Copy Markdown
ContributorAuthor

Thanks for this security fix from the Code Scanning Fixer workflow! 🔒 The change is surgical and well-reasoned — wrapping ownerId, projectId, and repoId with the existing escapeGraphQLString() helper achieves consistent defense-in-depth across all three GraphQL mutation arguments in pkg/cli/project_command.go.

One thing that would strengthen this PR:

  • Add tests — the fix itself is minimal and correct, but pkg/cli/project_command.go currently has no test coverage for the escaping behavior. Adding a unit test that exercises createProject and linkProjectToRepo with a node ID that contains special characters (e.g. a value with " or \) would verify the escaping is applied and prevent regressions.

If you'd like a hand adding that coverage, you can assign this prompt to your coding agent:

Add unit tests to pkg/cli/project_command_test.go (create it if it doesn't exist) covering the GraphQL escaping behavior in project_command.go.
Specifically, test the following scenarios:
1. `createProject` called with an `ownerId` containing a double-quote character — verify the resulting GraphQL mutation string has it escaped.
2. `linkProjectToRepo` called with a `projectId` and/or `repoId` containing a backslash — verify both are escaped in the resulting mutation.
Use the existing `escapeGraphQLString` helper as the reference for expected output. Mock or stub the `workflow.RunGH` call so no real API calls are made.

Generated by ✅ Contribution Check · 143.1 AIC · ⌖ 19.3 AIC · ⊞ 5.9K ·

…ing interpolation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
CopilotAI changed the title [code-scanning-fix] Fix workflow-graphql-id-unescaped: escape node IDs in GraphQL mutations[code-scanning-fix] Fix workflow-graphql-id-unescaped: eliminate string interpolation in GraphQL mutations via variablesJun 22, 2026
CopilotAI requested a review from pelikhanJune 22, 2026 14:55
@pelikhan
pelikhan merged commit 716a532 into mainJun 22, 2026
36 checks passed
@pelikhan
pelikhan deleted the fix/graphql-id-unescaped-627-628-d08e45c37cbf4f35 branch June 22, 2026 17:02
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@pelikhan