Do not open a public issue.
Use GitHub's private vulnerability reporting, which goes only to the maintainers.
If that is unavailable to you, email jeelgajera200@gmail.com with SECURITY in the
subject.
Please include what you can: the version or commit, what an attacker gains, and the smallest reproduction you have. A rough report sent early beats a polished one sent late.
| Acknowledgement | Within 3 working days |
| Initial assessment | Within 7 days |
| Fix or mitigation plan | Communicated with the assessment |
| Credit | Offered in the advisory unless you prefer otherwise |
This is a solo-maintained project, not a company with an on-call rota. If something is being actively exploited, say so and I will treat it accordingly.
- Webhook signature bypass
- Anything that lets an unauthorised person run a
/dispatchcommand - Leaking a provider key, a GitHub token, or an installation secret — including into logs, comments, decision logs, or recorded fixtures
- Privilege escalation through a crafted issue, comment, pull request, or config
- Any path by which Dispatch executes attacker-controlled code
- Redaction failing to strip what
data.redactsays it strips
- The model returning a wrong answer. Miscalibration is a quality bug, not a
vulnerability — report it as a normal issue with
dispatch evaloutput. - Vulnerabilities in a dependency with no exploitable path through Dispatch. Report those upstream; a Dependabot alert on its own is not a finding.
- Anything requiring an already-compromised maintainer account or runner.
- A
pull_request_targetworkflow that someone added a checkout step to. That is documented as a repository takeover in three places; doing it anyway is a misconfiguration, not a vulnerability here.
These are the promises. If you can break one, that is a finding.
HMAC-SHA256 over the raw request body, compared with timingSafeEqual, with a
length check first because timingSafeEqual throws on a mismatch and that throw is
itself an oracle. Re-serialising a parsed body changes key order and whitespace; the
verifier never sees a re-encoded payload.
/dispatch commands run only for OWNER, MEMBER, or COLLABORATOR, taken from the
author_association GitHub puts on the comment. A command must be alone on its line,
and commands inside markdown quotes are ignored — otherwise quoting someone's comment,
or writing "you can run /dispatch undo to revert this", would execute it.
An unrecognised verb is rejected rather than falling back to plain triage.
Dispatch reads pull request metadata through the API. It never checks out a head, never
runs a build, and never evaluates anything from a contributor's branch. This is what
makes the pull_request_target sample safe, and the sample carries the warning inline.
Every pattern in data.redact is applied on the way out, including into recorded
fixtures — the recorder asks the provider what it actually sent rather than recording
what it was handed. A test plants a credential-shaped string and asserts it does not
appear in any cassette.
Logs redact fields whose names look like credentials.
In the App, each installation's provider key is sealed with AES-256-GCM under a master key held in the environment and never in the database. GCM specifically for the authentication tag: tampered ciphertext would otherwise decrypt to garbage and then be sent to the provider as an API key. A dump of the database alone does not let an attacker spend a maintainer's provider budget.
The App manifest requests contents: read, issues: write, pull_requests: write,
metadata: read. It does not request contents: write, administration, or
members.
close, lock, minimize and convert_to_discussion are suppressed unless the
operation is named in allowDestructive. No shipped default enables one, and a test
asserts that across the whole fixture corpus.
Documented in full in the privacy guide and above the fold in the README. In short: the item being triaged and its repository context — not your repository contents, not files you did not change, not your other repositories.
Setting data.sendBodies: false produces a working, weaker triage from structural
signals alone, and is covered by a test.
Pre-1.0. Security fixes land on master and in the next release; there are no
maintained backport branches yet. Once 1.0 ships, the current major will receive fixes.
- Pin the Action to a commit rather than a moving tag
- Keep
mode: shadowuntildispatch evalshows calibration you believe - Leave
allowDestructiveempty unless you have a specific reason - Set
budget.maxRequestsPerDayso a runaway loop has a ceiling - Self-host the App if issue text must not leave your infrastructure