feat: tag auto-moved lab issues with source="extensions"#58
Conversation
swamp-club#375 added a `source` field to lab issues so each one is attributed to its origin (web app, CLI, extensions, UAT). The auto-responder was defaulting to "swamp-club"; tag it with `source: "extensions"` so the Lab UI shows the correct chip and filter bucket. Switches the endpoint from `/api/v1/lab/issues/ensure` (admin-only, doesn't accept `source`) to the public `/api/v1/lab/issues` endpoint. The footer is expanded to include the full GitHub issue URL so the back-link survives the loss of the structured githubRepoFullName / githubIssueNumber fields the public endpoint doesn't accept. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
Blocking Issues
None.
Suggestions
-
Title truncation:
issue.titleis sent without truncation; if swamp-club'sMAX_TITLE_LENGTHis 256 chars and a GitHub title exceeds that, the API call will fail withcore.setFailed. The PR notes acknowledge this is unchanged behavior — worth a follow-up to truncate client-side to avoid leaving GitHub issues unclosed on failure. -
githubAuthorLoginremoved: The field was dropped since the public endpoint doesn't require it. If the lab UI uses author attribution, this will silently lose that data. Low risk given the PR description, but worth verifying against the swamp-club endpoint contract.
Changes look correct and well-reasoned. The switch to the public /issues endpoint with source: "extensions" is straightforward. Error handling, null-safety on response parsing, and secret handling are all appropriate.
There was a problem hiding this comment.
CI Security Review
Critical / High
None.
Medium
None.
Low
None.
Analysis
Prompt Injection (N/A): No LLM invocations in this workflow.
Expression Injection: No run: blocks — all logic runs inside actions/github-script, which executes JavaScript (not shell). User-controlled fields (issue.title, issue.body, issue.html_url) are accessed as JS object properties and passed through JSON.stringify(), which properly escapes them before they reach the external API. No expression injection vector.
Dangerous Triggers: The issues: opened trigger is externally reachable, but the workflow only reads issue data via the JS context object and sends it to a hardcoded URL (https://swamp.club/api/v1/lab/issues) over HTTPS. An attacker opening an issue cannot redirect data flow or exfiltrate secrets.
Supply Chain: actions/github-script@v7 is a GitHub-owned action with a tag pin — acceptable per policy.
Permissions: issues: write + contents: read at the workflow level. Since there is only one job, workflow-level scoping is equivalent to job-level. Permissions are minimal for the job's needs (post comments, close issues).
Secret Exposure: SWAMP_CLUB_API_KEY is passed via env: (not interpolated in shell) and used only in an Authorization header sent to a hardcoded HTTPS endpoint. Error paths log the HTTP status and response body from the external API, not the secret itself.
Auto-merge: Not applicable — no merge behavior in this workflow.
Changes are security-neutral. The PR swaps the API endpoint from /ensure to /issues, adds a source field, and adjusts the footer text. All existing safe patterns (JSON.stringify for serialization, env-var for secrets, hardcoded target URL) are preserved.
Verdict
PASS — No security findings. The workflow changes maintain existing safe patterns and introduce no new attack surface.
Summary
swamp-club#375 added a
sourcefield on lab issues so each one is attributed to its origin (swamp-club,swamp,extensions,UAT). This auto-responder was defaulting toswamp-club, which is wrong for issues filed againstsysteminit/swamp-extensions. Tag submissions withsource: "extensions"so the Lab UI shows the correct chip and filter bucket.Switches the endpoint from the admin-only
/api/v1/lab/issues/ensure(which doesn't acceptsource) to the public/api/v1/lab/issuesendpoint, and expands the footer to include the full GitHub issue URL (issue.html_url) so the back-link survives the loss of the structuredgithubRepoFullName/githubIssueNumberfields that the public endpoint doesn't accept.Mirrors systeminit/swamp#1140, which makes the same change for the swamp repo (with
source: "swamp").Notes
/issuesendpoint accepts the sameBearer ${SWAMP_CLUB_API_KEY}header as/ensure, confirmed via swamp-club'sroutes/_middleware.tswhich recognizesBearer swamp_…API keys for any/api/*route./issuesis not idempotent on(repo, issueNumber)like/ensurewas, but this workflow only fires onissues.opened(one-shot per GitHub issue), so dedup behavior is unchanged in practice.MAX_TITLE_LENGTHis 256 chars; GitHub titles can exceed this. The current workflow doesn't truncate — same as before. Failures keep the GitHub issue open viacore.setFailed.Test plan
systeminit/swamp-extensionsand verify the auto-responder produces a lab issue with the "Extensions" chip and a footer linking back to the GitHub URL🤖 Generated with Claude Code