Skip to content

no-github-request-interpolated-route: rule never scope-resolves the route argument — identifier-bound dynamic routes bypass dete [Content truncated due to length] #49913

Description

@github-actions

Rule

eslint-factory/src/rules/no-github-request-interpolated-route.ts

Design gap

getInterpolatedRouteKind() is called directly on firstArg of <client>.request(firstArg, ...). It only recognizes an inline TemplateLiteral/BinaryExpression — it never resolves an Identifier first argument back to its initializer. Contrast with the sibling command rules in the same factory, no-exec-interpolated-command and no-child-process-interpolated-command, which both call resolveWriteOnceInitializerChain() (src/rules/command-initializer-utils.ts) specifically so that const cmd = \git ${branch}`; exec.exec(cmd)is still caught.no-github-request-interpolated-route` has no equivalent step, so:

const route = `GET /repos/${owner}/${repo}${suffix}`;
github.request(route, {});

... is silently unflagged today, even though the inline form github.request(\GET /repos/${owner}/${repo}${suffix}`, {})is correctly flagged asinterpolatedRoute`.

Why this matters / grounding

The "extract a route into a local variable" idiom is already established in this exact codebase — create_project.cjs:230 and update_project.cjs:919 both do:

const route = projectInfo.scope === "orgs" ? "POST /orgs/{org}/projectsV2/{project_number}/views" : "POST /users/{user_id}/projectsV2/{project_number}/views";
...
const response = await github.request(route, params);

This particular instance is fully static (both ternary branches are literals) so it's a correct true-negative today. But it proves the const route = ...; client.request(route, ...) shape is a normal, encouraged pattern here — and the rule's own opaqueWholeRoute message text ("pass a typed route string from the caller when the entire route is dynamic") actively steers developers toward exactly the identifier-bound shape the rule cannot see through. The very first time someone builds that caller-provided route dynamically (e.g. via template literal or concatenation) instead of a static literal, the rule goes blind.

Ask

Give no-github-request-interpolated-route the same write-once scope resolution the exec/child_process rules already have, so an Identifier first argument is resolved to its initializer (reusing or adapting resolveWriteOnceInitializerChain, ideally factored into a rule-agnostic shared util since three rules now need it) before classifying static vs. interpolated vs. concatenated.

Acceptance criteria

  • When the first argument to <client>.request() is a plain Identifier, the rule walks its write-once initializer chain (function-scoped, single-definition, no reassignment — matching the existing resolveWriteOnceInitializerChain semantics) before running getInterpolatedRouteKind/isOpaqueWholeRouteInterpolation.
  • New passing test: const route = \GET /repos/${owner}/${repo}`; github.request(route, {})→ reportsinterpolatedRoute`.
  • New passing test: const route = cond ? "GET /a" : "GET /b"; github.request(route, {}) → NOT flagged (fully static ternary stays a true negative, matching the live create_project.cjs/update_project.cjs idiom).
  • New passing test: identifier reassigned after declaration (let route = ...; route = otherExpr;) → not resolved through (write-once semantics preserved, matches resolveInitializer's existing reassignment guard).
  • Existing valid/invalid test suite for this rule remains green; behavior for inline (non-Identifier) route arguments is unchanged.

Scope

eslint-factory/src/rules/no-github-request-interpolated-route.ts (+ test file). No production code in actions/setup/js/** needs to change — this is a rule-only false-negative fix; the only live call sites found (add_reaction.cjs, add_reaction_and_edit_comment.cjs, add_workflow_run_comment.cjs, assign_agent_helpers.cjs, create_project.cjs, update_project.cjs, route_slash_command.cjs, update_activation_comment.cjs, notify_comment_error.cjs, autofix_code_scanning_alert.cjs, close_issue.cjs, create_agent_session.cjs) already use static or safely-typed routes.

Generated by 🤖 ESLint Refiner · agent · 224.3 AIC · ⌖ 28.6 AIC · ⊞ 4.9K · ◷

  • expires on Aug 9, 2026, 10:29 PM UTC-08:00

Activity

  1. github-actions commented on Aug 3, 2026

    @github-actions
    ContributorAuthor

    🍪 Issue Monster selected this for Copilot

    I have identified this issue as a good candidate for automated resolution and requested assignment to the Copilot coding agent.

    If assignment succeeds, the Copilot coding agent will analyze the issue and create a pull request with the fix.

    Om nom nom! 🍪

    🍪 Om nom nom by Issue Monster · gpt54 · 3.94 AIC · ⌖ 5.41 AIC · ⊞ 8.9K · ◷

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions