Skip to content

eslint-factory: no-github-request-interpolated-route — scope-resolution false negatives (global.getOctokit client alias + route- [Content truncated due to length] #45206

Description

@github-actions

Rule

no-github-request-interpolated-route (eslint-factory/src/rules/no-github-request-interpolated-route.ts) — first review since it shipped (documented in the README; not previously covered by an issue).

Summary

The rule is sound and works on live code: it correctly flags the value-into-path interpolations at actions/setup/js/route_slash_command.cjs:180,192,204,216 (e.g. github.request(`POST /repos/${owner}/${repo}/issues/${issueNumber}/reactions`, ...)) — genuine true positives that should use the typed {owner}/{repo}/{issue_number} placeholder form. No live false positives were found.

However the client/route resolution has two false-negative gaps, one of them grounded in this codebase's actual Octokit-client factory.

Gap 1 (grounded) — global.getOctokit(...) is not recognized as an Octokit source

isOctokitSourceExpression recognizes a client obtained from getOctokit(...) (bare identifier callee) and from the member forms github.getOctokit(...) / actions.getOctokit(...) (GET_OCTOKIT_MEMBER_OBJECT_NAMES = {"github", "actions"}). It does not recognize global.getOctokit(...).

But global.getOctokit(token) is precisely the client factory this codebase uses:

  • actions/setup/js/setup_globals.cjs:51-65 installs a wrapped global.getOctokit.
  • actions/setup/js/create_pull_request.cjs:85return global.getOctokit(token);
  • actions/setup/js/create_agent_session.cjs:37return global.getOctokit(token);

So a non-well-known alias of that result escapes detection entirely:

const gh = global.getOctokit(token);   // `gh` is not in OCTOKIT_CLIENT_NAMES,
gh.request(`GET /repos/${owner}/${repo}`); // and `global.getOctokit` is unrecognized → NOT flagged (FN)

(Today the live call sites store the result under the well-known name octokit/github, so the name short-circuit still catches them — but any const gh = .../const client = ... alias of global.getOctokit(token) would silently pass.)

Fix: add "global" to GET_OCTOKIT_MEMBER_OBJECT_NAMES (or resolve global.getOctokit(...) explicitly).

Gap 2 (latent, asymmetry) — the route argument is never scope-resolved, though the client is

resolveOctokitClientName / isIdentifierBoundToOctokitClient follow a single-hop const alias for the client, but the route argument is inspected purely syntactically: an Identifier route is never resolved to its initializer. So:

const route = `GET /repos/${owner}/${repo}`; // interpolated
github.request(route, params);               // NOT flagged (FN)

This is the same class of indirection the rule already handles for the client, so the asymmetry is surprising. Grounded-adjacent: actions/setup/js/update_project.cjs:877 and create_project.cjs:252 both call github.request(route, params) with a variable route — safe today only because route is a const bound to a conditional of two static string literals (update_project.cjs:855), which the analyzer cannot currently distinguish from an interpolated one.

Fix: when the route argument is an Identifier, resolve a single-hop const initializer and apply the existing isInterpolatedTemplateLiteral / isStringConcatenation checks to it; static-literal consts (via isStaticRouteExpression) remain accepted.

Acceptance criteria

  • const gh = global.getOctokit(token); gh.request(`` GET /x/${y} ``) is flagged; global is added to the getOctokit member-object allowlist with a regression test.
  • An Identifier route argument bound to a single-hop const interpolated template / concatenation is flagged; a const bound to a static literal (or literal concatenation) is not flagged.
  • New unit tests cover both gaps; existing valid/invalid cases and the docs-URL test remain green.
  • Documented out-of-scope forms (this.github.request(...), .concat()-built routes, multi-hop/reassigned aliases) stay out of scope and are noted in the test comments.

Generated by 🤖 ESLint Refiner · 411.7 AIC · ⌖ 13 AIC · ⊞ 4.6K ·

  • expires on Jul 19, 2026, 10:32 PM UTC-08:00

Metadata

Metadata

Labels

cookieIssue Monster Loves Cookies!eslint

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