Skip to content

[uk-ai-resilience] [security] Fix GraphQL ID injection in project_command.go (Semgrep #627/#628, CWE-89) #43240

Description

@github-actions

Summary

Semgrep alerts #627 and #628 (both created 2026-06-16, open 17 days) flag unescaped GraphQL ID injection in two mutations in the project command.

File: pkg/cli/project_command.go lines 295 and 368
Rule: workflow-graphql-id-unescaped (CWE-89 — injection)
Severity: WARNING (defense-in-depth gap)

Tier & Risk Scoring

Dimension Score Notes
Exposure amplification 3 CLI command used for project management; affects any user invoking project commands
Patchability 2 Two call sites; wrap with existing helper or parameterise
Detectability 3 GitHub API node IDs are currently opaque; special chars unlikely but not impossible
Operational fragility 3 Malformed query could fail silently or be exploited if ID format changes
Ownership confidence 4 Active CLI team ownership
Aggregate 15 Tier B — Open With Conditions

SLA: High — fix within 7 days.

Root Cause

Alert #627createProject() (line 295)

ownerId is injected directly into the GraphQL mutation string via fmt.Sprintf without escapeGraphQLString(). The title argument on the same call (line 303) is already escaped — creating an inconsistency. If GitHub ever returns a node ID with special characters, this could trigger GraphQL injection.

Alert #628linkProjectToRepo() (line 368)

Both projectId and repositoryId are injected without escapeGraphQLString(). These values come from prior API calls, but are not escaped before interpolation into the mutation string.

Recommended Fix

Option A — wrap with existing helper:

// line 295 — createProject()
query := fmt.Sprintf(`mutation { createProject(input: {ownerId: "%s", title: "%s"}) { ... } }`,
    escapeGraphQLString(ownerId),  // add escape
    escapeGraphQLString(title))

// line 368 — linkProjectToRepo()
query := fmt.Sprintf(`mutation { linkProjectV2ToRepository(input: {projectId: "%s", repositoryId: "%s"}) { ... } }`,
    escapeGraphQLString(projectId),    // add escape
    escapeGraphQLString(repositoryId)) // add escape

Option B — parameterise via GraphQL variables (preferred):

gh api graphql -f query='mutation($ownerId:ID!,$title:String!){createProject(input:{ownerId:$ownerId,title:$title}){...}}' \
  -f ownerId="$ownerId" -f title="$title"

Governance Context

Identified by the UK AI Open Code Risk & Resilience Governance scan (2026-07-03). See discussion report for full tier classification and remediation queue.

References: Semgrep alert #627 · Semgrep alert #628 · run 28671336499

Generated by UK AI Operational Resilience · 91.2 AIC · ⌖ 8.39 AIC · ⊞ 5.2K ·

Metadata

Metadata

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions