[code-scanning-fix] Fix GraphQL injection in getOwnerNodeId (alerts #651 and #652) - #47952
Conversation
Replace fmt.Sprintf string interpolation with GraphQL query variables in getOwnerNodeId() to eliminate GraphQL injection risk. The owner login is now passed as a variable (-f login=<owner>) instead of being embedded directly into the query string. This matches the pattern already used in validateOwner() and eliminates alerts #651 and #652 (workflow-go-graphql-injection-sprintf). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
✅ Design Decision Gate 🏗️ completed the design decision gate check. No ADR enforcement needed: PR does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (only 3 new lines detected in default business directories). |
|
✅ PR Code Quality Reviewer completed the code quality review. |
|
✅ Test Quality Sentinel completed test quality analysis. No test files were added or modified in this PR. Test Quality Sentinel skipped. |
|
🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅ |
There was a problem hiding this comment.
Pull request overview
Replaces unsafe GraphQL interpolation in owner ID lookup with query variables.
Changes:
- Uses static parameterized queries for organization and user lookups.
- Passes owner login via
gh api graphql -f.
Show a summary per file
| File | Description |
|---|---|
pkg/cli/project_command.go |
Parameterizes owner ID GraphQL queries. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Medium
| } | ||
|
|
||
| output, err := workflow.RunGH("Getting owner ID...", "api", "graphql", "-f", "query="+query, "--jq", jqPath) | ||
| output, err := workflow.RunGH("Getting owner ID...", "api", "graphql", "-f", "query="+query, "-f", "login="+owner, "--jq", jqPath) |
There was a problem hiding this comment.
Security Fix Review
The fix correctly replaces brittle fmt.Sprintf string interpolation with parameterized GraphQL variables, eliminating the injection risk flagged by alerts #651 and #652.
What was changed:
- Both
organizationanduserqueries now usequery($login: String!) { ... }with a$loginvariable owneris passed safely via-f login=<owner>togh api graphql- This matches the pattern already used in
validateOwner()in the same file
Assessment: The fix is correct, minimal, and consistent with existing safe patterns in the codebase. No blocking issues found.
🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 11.5 AIC · ⌖ 4.52 AIC · ⊞ 5K
There was a problem hiding this comment.
Approved
Clean, minimal security fix. Replacing fmt.Sprintf + escapeGraphQLString with parameterized GraphQL variables and -f login=owner is the correct approach — it eliminates the injection class entirely. The fix matches the pattern already used in validateOwner() in the same file.
🔎 Code quality review by PR Code Quality Reviewer · sonnet46 · 10.8 AIC · ⌖ 4.49 AIC · ⊞ 5.7K
Comment /review to run again
There was a problem hiding this comment.
Skills-Based Review 🧠
Applied /diagnosing-bugs — the fix is correct and consistent with existing patterns; one comment on test coverage.
📋 Key Themes & Highlights
Key Themes
- Correct fix: replacing
fmt.Sprintf+escapeGraphQLStringwith GraphQL variables (-f login=...) is the right approach and matchesvalidateOwner()on line 241. - Missing regression test: no test was added to lock in the parameterized pattern for special-character owner names.
Positive Highlights
- ✅ Fix is minimal, surgical, and consistent with the existing codebase pattern
- ✅ Both org and user branches fixed symmetrically
- ✅
escapeGraphQLStringdead code is removed implicitly (the brittle manual escaping is no longer needed for this path)
🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 18.3 AIC · ⌖ 4.66 AIC · ⊞ 6.7K
Comment /matt to run again
| } | ||
|
|
||
| output, err := workflow.RunGH("Getting owner ID...", "api", "graphql", "-f", "query="+query, "--jq", jqPath) | ||
| output, err := workflow.RunGH("Getting owner ID...", "api", "graphql", "-f", "query="+query, "-f", "login="+owner, "--jq", jqPath) |
There was a problem hiding this comment.
[/diagnosing-bugs] No regression test covers getOwnerNodeId with special-character owner names — the exact scenario this injection fix guards against.
Without a test, a future refactor could silently reintroduce string interpolation and the code scanner would be the only safety net.
💡 Suggested test skeleton
func TestGetOwnerNodeId_SpecialChars(t *testing.T) {
// Swap projectCommandRunGH with a mock that captures args.
// Assert query contains "\$login" (not a literal owner string),
// and that "-f", "login=<special-char-owner>" appears in args.
}This locks in the parameterized-variable pattern and prevents regression.
@copilot please address this.
|
@copilot please run the
|
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Addressed the unresolved review feedback in commit b99be13 by routing |
|
🎉 This pull request is included in a new release. Release: |
Summary
Fixes GraphQL injection vulnerabilities in
getOwnerNodeId(code scanning alerts #651 and #652). Theownerlogin value was previously interpolated directly into GraphQL query strings usingfmt.Sprintf, bypassing theescapeGraphQLStringhelper in some edge cases and still remaining vulnerable to injection. This PR replaces string interpolation with parameterized GraphQL variables, passing the login value via-f login=<owner>sogh api graphqltransmits it as a typedString!variable.Changes
pkg/cli/project_command.gofmt.Sprintf-based query construction ingetOwnerNodeIdwith static parameterized queries using$login: String!.workflow.RunGHtoprojectCommandRunGHand added-f login=<owner>as an explicit variable argument.escapeGraphQLStringcall — no longer needed since the value is passed out-of-band as a variable.pkg/cli/project_command_test.goTestGetOwnerNodeIdUsesStringLoginFieldcovering org and user owner types.false,null, special characters) that would previously risk injection or type coercion.query=andlogin=args are passed with-f, and that the correct--jqpath is forwarded.Security Impact
Previously, a crafted
ownervalue could escape the query string and inject arbitrary GraphQL. With parameterized variables, the login is always transmitted as a string and never interpreted as query syntax.Testing
New unit tests in
TestGetOwnerNodeIdUsesStringLoginFieldverify the parameterized variable path for both org and user owner types, including special-character inputs.