Skip to content

Roll the \translate-resync trust gate out to the production source repos (#192 step 3) #220

Description

@mmcky

Rollout step 3 of #192: the four deployed sync workflows in the production source repos still carry the ungated \translate-resync trigger. #219 fixes the canonical templates in this repo — it does not touch anything already deployed, and there is no mechanism by which it would. These four need the edit applied by hand, in their own repos.

Why this is not "already fixed by #219"

The action pins @v0, so deployed workflows do pick up new action code — but the gate is in the workflow file, not in the action. src/inputs.ts has always enforced the same three-way association set internally, so a hostile resync is rejected inside the run; the run still starts, still checks out, still resolves and loads the action bundle, and still holds ANTHROPIC_API_KEY and the services PAT in its environment while doing so. Runner minutes are spent per comment, and they are spent as fast as someone can comment. The workflow-level gate is the only thing that stops the job from starting at all.

All three repos are public (lecture-python-intro 31 forks, lecture-python-programming 30, lecture-python.myst 58), so "any GitHub account" is the real audience, not a hypothetical one.

Current state — audited 2026-07-26

Repo / workflow on a PR? trusted author? permissions: status
lecture-python-intro / sync-translations-zh-cn.yml needs fix
lecture-python-programming / sync-translations-zh-cn.yml needs fix
lecture-python-programming / sync-translations-fa.yml needs fix
lecture-python-programming / sync-translations-fr.yml needs fix
lecture-python.myst / sync-translations-zh-cn.yml done (8abdb57) — this is the reference shape
test-translation-sync / sync-translations-{zh-cn,fa,ml}.yml step 2 — self-heals, see below

Audited by fetching each file from its default branch and checking for github.event.issue.pull_request, author_association and a job-level permissions: block. The set is complete: an org-wide code search for translate-resync returns these eight workflows and nothing else outside this repo.

The change

Two edits per file. Replace the issue_comment clause of the job's if:, and add a job-level permissions: block after runs-on:.

jobs:
  sync:
    # The issue_comment path requires all three: a comment on a PR (not a bare
    # issue), the command, and a trusted author — otherwise any account could
    # fire a secrets-bearing run (Anthropic spend plus the PAT) from any comment.
    if: >
      (github.event_name == 'pull_request' && github.event.pull_request.merged == true) ||
      (github.event_name == 'issue_comment' &&
       github.event.issue.pull_request &&
       contains(github.event.comment.body, '\translate-resync') &&
       contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association))
    runs-on: ubuntu-latest

    # The action authenticates with QUANTECON_SERVICES_PAT; the ambient
    # GITHUB_TOKEN is unused beyond checkout, so keep it read-only.
    permissions:
      contents: read

    steps:
      ...

Nothing else in these files changes — same triggers, same paths:, same inputs, same @v0 pin.

Note the secret name in that comment: the production repos authenticate with QUANTECON_SERVICES_PAT, whereas the canonical templates in this repo say TRANSLATION_PAT. Neither is wrong; only the comment differs.

Checklist

The three lecture-python-programming files can go in one PR; lecture-python-intro needs its own.

Verification, and the one thing worth actually testing

The if: uses a folded scalar whose continuation lines are more-indented, so YAML preserves those newlines rather than folding them to spaces — the expression GitHub evaluates contains literal newlines. That is fine, and it is not theory: this exact shape has been live on lecture-python.myst since 2026-07-22 and both branches have fired since (issue_comment runs on 07-23, pull_request runs through 07-26). Still worth confirming per repo after the edit, because the failure mode is silent — a broken if: does not error, it simply stops the workflow firing on merges, which looks exactly like "no PRs have been merged lately".

Cheapest confirmation, in order: (1) GitHub's own workflow linter rejects a malformed if: on push, so a clean push is already evidence; (2) merge any PR touching lectures/ and check the run appears; (3) comment \translate-resync on a recent merged PR as a trusted user and confirm it fires, then confirm a comment lacking the command does not.

Related work, deliberately not in scope here

Step 2, the harness (test-translation-sync) is not a manual edit. Those three workflows are rendered and force-pushed from tool-test-action-on-github/test-action-on-github-data/sync-workflow-template.yml, which #219 fixes — so they pick the gate up on the next harness run, and a hand-edit would be overwritten. Until then they are ungated (they already require issue.pull_request, so the exposure is narrower). The harness repo is public but exists to be reset, so the cost exposure is bounded by whoever notices it.

Target repos (*.zh-cn, *.fa, *.fr, *.ml) are unaffected: their review and rebase workflows have no issue_comment trigger and already carry permissions: blocks from examples/.

Refs #192, #219, #138. Original sightings: QuantEcon/test-translation-sync#671 and QuantEcon/lecture-python.myst#979.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghigh-priorityAddress soonsecuritySecurity implications — needs a security-aware review bar

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions