Skip to content

fix(workflows): pass retro metrics via env to avoid JS template injection#1012

Merged
sabbour-squad-lead[bot] merged 1 commit into
mainfrom
squad/792-workflow-fixes
Apr 21, 2026
Merged

fix(workflows): pass retro metrics via env to avoid JS template injection#1012
sabbour-squad-lead[bot] merged 1 commit into
mainfrom
squad/792-workflow-fixes

Conversation

@sabbour-squad-lead
Copy link
Copy Markdown
Contributor

Summary

The squad-pr-retro.yml Mirror line as PR comment step embedded ${{ steps.metrics.outputs.line }} directly inside a JavaScript template literal. When a PR title contained backticks (e.g. PR #982 "fix(emit_ui): use `component`+`id` fields …"), the backticks terminated the template literal and the remainder parsed as JS, failing with SyntaxError: Unexpected identifier 'component' (see run 24713769544).

Root cause

GitHub Actions expression substitution inside a JS string literal is unsafe — any metacharacter in the PR title breaks parsing, and worse, lets a PR author inject arbitrary JS into a step that runs with the Scribe app token.

Fix

Pass line, retro-PR number and URL via the step's env: block and read them with process.env in the github-script body. Same pattern already used elsewhere in this workflow ("Append to retro log" step at line ~247).

Verification

  • Reproduced the exact Unexpected identifier 'component' error locally with the old code and a PR title containing backticks.
  • Confirmed new code reads the same string safely via process.env.RETRO_LINE.
  • YAML parses (python3 -c 'import yaml; yaml.safe_load(...)').
  • npm run lint → 0 errors.
  • CI=1 npm test → 1006 passed, 3 skipped, 159 todo.

Scope

Closes #792

Working as Bender (Backend Dev) — see .squad/agents/bender/charter.md.

…tion

The 'Mirror line as PR comment' step in squad-pr-retro.yml embedded
${{ steps.metrics.outputs.line }} directly inside a JavaScript
template literal. When a PR title contained backticks (e.g. PR #982
'fix(emit_ui): use `component`+`id` fields ...'), the backticks
terminated the template literal and the remaining tokens became
invalid JS, failing with 'SyntaxError: Unexpected identifier'.

Root cause: GitHub Actions expression substitution inside a JS
string literal is unsafe — any metacharacter in the PR title breaks
parsing and, worse, allows script injection from PR titles.

Fix: pass the line and retro-PR metadata through env vars and read
them via process.env in the github-script step. Same pattern already
used elsewhere in this workflow (see the 'Append to retro log' step).

Release-cadence was already fixed on main in 7f708d6; this PR
completes #792 by fixing the remaining pr-retro failure.

Closes #792

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 21, 2026

👀 Squad review trail

Current head: 30b1aa8
Last update: Label applied: nibbler:approved.

  • Native PR review mirror created for this label event.
    Gate path: Standard path — leela:approved + zapp:approved + nibbler:approved are required on the current head, plus one of docs:approved or docs:not-applicable for the docs gate.
    Gate snapshot:squad/review-gate should be green on the current head.
    Reviewer labels
  • Leela: ✅ approved via leela:approved
  • Zapp: ✅ approved via zapp:approved
  • Nibbler: ✅ approved via nibbler:approved
  • Docs: ➖ not applicable via docs:not-applicable
    Active labels
  • docs:not-applicable — Docs gate satisfied without docs changes — DP explicitly declared Docs impact: N/A
  • leela:approved — Architecture review approved
  • nibbler:approved — Code quality review approved
  • zapp:approved — Security review approved

This sticky comment is maintained automatically so label-based squad review leaves an on-PR rationale even when the gate itself is status-check driven.

@github-actions
Copy link
Copy Markdown
Contributor

Docs & changeset gate

  • ℹ️ no changeset found (.changeset/*.md)
  • ℹ️ docs-site/docs/ not updated — consider updating if user-facing behavior or UI changed
  • ℹ️ docs-site/docs/extending/api-endpoints.md not updated — consider updating if the API surface changed

No user-facing source changed. Changeset and doc updates are not needed.


Hard gate for user-facing package changes without docs or changeset. ✅ = done, ⚠️ = likely needed, ℹ️ = optional or bypassed.

@sabbour-squad-lead sabbour-squad-lead Bot added docs:not-applicable Docs gate satisfied without docs changes — DP explicitly declared Docs impact: N/A squad:bender Assigned to Bender (Backend Dev) labels Apr 21, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

➖ Docs recorded a documentation not applicable via docs:not-applicable on head 30b1aa8.

This native review mirrors the label-driven squad gate for visibility only.
Merge eligibility still comes from the squad/review-gate status check and the current approval labels.

@sabbour-squad-lead
Copy link
Copy Markdown
Contributor Author

Leela Code Review — APPROVED

#1012: Fix broken workflows (squad-pr-retro JS template injection)

Checklist

Approved and ready to merge.

@sabbour-squad-lead sabbour-squad-lead Bot added the leela:approved Architecture review approved label Apr 21, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Leela recorded a architecture approved via leela:approved on head 30b1aa8.

This native review mirrors the label-driven squad gate for visibility only.
Merge eligibility still comes from the squad/review-gate status check and the current approval labels.

Copy link
Copy Markdown
Owner

@sabbour sabbour left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approve — correct fix, right pattern

Working as Nibbler (Code Reviewer & Watchdog).

Live check of head 30b1aa8 — all required checks green. Diff is surgical: 10 lines across one workflow file.

What's good

  • Correct root-cause fix. Interpolating ${{ … }} directly into a JS template literal is a real injection surface — a backtick or ${…} in a PR title either breaks parsing or executes arbitrary JS in a step that runs with the Scribe token. Moving the values into env: and reading them via process.env.* is exactly the right pattern and matches what the "Append to retro log" step in this same workflow already does (consistency ✔️).
  • No silent catches. Straightforward ?? '' fallbacks — a missing output yields an empty string, not a thrown error in a notification-only comment. Acceptable.
  • Scope is tight. No unrelated changes; PR description correctly notes the companion fix in squad-release-cadence.yml already shipped via #973.

Nits (non-blocking)

  • 🟢 No new test — fair, since this is a workflow-only YAML change and the failure mode reproduces only inside actions/github-script. The reproducer in the description is sufficient.
  • 🟢 RETRO_PR_NUMBER / RETRO_PR_URL defaulting to '' means a broken upstream step would render Queued via retro-log PR #: in the comment. Ugly but not dangerous; not worth a round-trip.

Verdict

Applying nibbler:approved. Ready to merge once other gates clear.

@sabbour-squad-lead sabbour-squad-lead Bot added the zapp:approved Security review approved label Apr 21, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Zapp recorded a security approved via zapp:approved on head 30b1aa8.

This native review mirrors the label-driven squad gate for visibility only.
Merge eligibility still comes from the squad/review-gate status check and the current approval labels.

@sabbour-squad-lead
Copy link
Copy Markdown
Contributor Author

Zapp — Security review: APPROVE (posted as comment — API refuses self-approval on own PR)

Direct remediation of a JS template-injection vector. Verified:

  • steps.metrics.outputs.line, steps.retro_pr.outputs.number, steps.retro_pr.outputs.url are now exposed via env: and consumed as process.env.RETRO_LINE ?? '' etc., instead of being interpolated into the script source via ${{ ... }}.
  • ✅ The previous pattern was a classic JS injection sink — a metrics line containing a backtick, ${, or newline could escape the template literal and execute arbitrary JS with the Scribe token. Env-var passing makes the payload a plain opaque string; no eval/interpolation path.
  • ✅ No permission relax (same github-token: steps.squad_scribe_token.outputs.token).
  • ✅ No secret leak — only retro metrics lines & PR metadata are in scope, not logged by this step.
  • on: / permissions: / concurrency: unchanged.

zapp:approved label applied.

@sabbour sabbour added the nibbler:approved Code quality review approved label Apr 21, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Nibbler recorded a code quality approved via nibbler:approved on head 30b1aa8.

This native review mirrors the label-driven squad gate for visibility only.
Merge eligibility still comes from the squad/review-gate status check and the current approval labels.

@sabbour-squad-lead sabbour-squad-lead Bot merged commit ed335b6 into main Apr 21, 2026
31 checks passed
sabbour-squad-scribe Bot pushed a commit that referenced this pull request Apr 21, 2026
Working as Scribe — see .squad/agents/scribe/charter.md
@sabbour-squad-scribe
Copy link
Copy Markdown
Contributor

Working as Scribe · see .squad/agents/scribe/charter.md

Retro entry

- 2026-04-21 | #1012 "fix(workflows): pass retro metrics via env to avoid JS template injection" | S | impl=1m | review=16m | cycles=1 | merged | @sabbour-squad-lead[bot] | first_review=1m | ci=7m | reviewer=bot | human_comments=1 | issue=#792 | estimate=unknown | rejections_by_reviewer=nibbler:0,leela:0,zapp:0 | reverted=false

Queued via retro-log PR #1002: #1002
This update will land in .squad/retro-log.md after that PR merges.

sabbour-squad-lead Bot pushed a commit that referenced this pull request Apr 21, 2026
* chore(retro-log): #993 [scribe]

Working as Scribe — see .squad/agents/scribe/charter.md

* chore(retro-log): #1001 [scribe]

Working as Scribe — see .squad/agents/scribe/charter.md

* chore(retro-log): #1004 [scribe]

Working as Scribe — see .squad/agents/scribe/charter.md

* chore(retro-log): #1000 [scribe]

Working as Scribe — see .squad/agents/scribe/charter.md

* chore(retro-log): #1009 [scribe]

Working as Scribe — see .squad/agents/scribe/charter.md

* chore(retro-log): #1008 [scribe]

Working as Scribe — see .squad/agents/scribe/charter.md

* chore(retro-log): #1007 [scribe]

Working as Scribe — see .squad/agents/scribe/charter.md

* chore(retro-log): #1012 [scribe]

Working as Scribe — see .squad/agents/scribe/charter.md

* chore(retro-log): #1013 [scribe]

Working as Scribe — see .squad/agents/scribe/charter.md

* chore(retro-log): #1014 [scribe]

Working as Scribe — see .squad/agents/scribe/charter.md

* chore(retro-log): #1011 [scribe]

Working as Scribe — see .squad/agents/scribe/charter.md

---------

Co-authored-by: sabbour-squad-scribe[bot] <3414032+sabbour-squad-scribe[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs:not-applicable Docs gate satisfied without docs changes — DP explicitly declared Docs impact: N/A leela:approved Architecture review approved nibbler:approved Code quality review approved squad:bender Assigned to Bender (Backend Dev) zapp:approved Security review approved

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(workflow): fix squad-pr-retro and squad-release-cadence failures

1 participant