Skip to content

fix: gate issues.opened provisioning behind allowed_command_users + org membership#48

Open
avrabe wants to merge 1 commit intomainfrom
fix/issues-opened-auth-gate
Open

fix: gate issues.opened provisioning behind allowed_command_users + org membership#48
avrabe wants to merge 1 commit intomainfrom
fix/issues-opened-auth-gate

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 1, 2026

Fixes Bug #4 from docs/agent-fleet/bugs.md (wave-1 Security auditor — high severity).

The issue-form provisioning path had no auth gate. Anyone able to file a labelled issue could trigger org-repo creation. Adds the same allowlist + org-membership gate that comment-driven ChatOps already enforces.

  • 836 tests pass (+2)
  • eslint clean

🤖 Generated with Claude Code

…rg membership

## Why
Wave-1 Security auditor flagged this as Bug #4 in
`docs/agent-fleet/bugs.md`: comment-driven ChatOps already required
`requireOrgMember` AND `allowed_command_users`, but the parallel
issue-form provisioning path (`controller_repo`, Cut A) had no such
gate. Anyone able to file an issue with the configured `repo-request`
label could trigger real org-repo creation, including via the public
GitHub API.

## What
Two-layer gate inserted *before* validation/enqueue in the
`issues.opened` handler's controller_repo branch:

1. **`allowed_command_users`** — same allowlist that comment-driven
   ChatOps consults. Empty list = open to everyone (back-compat).
2. **`checkOrganizationMembership`** — defence in depth. The sender
   must be a member of the controller repo's owner org. The function
   already returns `false` on 404 / network errors, so an
   uncertain-membership case fails closed.

Each rejection posts a specific, actionable error comment back on
the issue. Sender is now read out of `context.payload.sender`.

## Source
Bug #4, wave-1 Security auditor (`docs/agent-fleet/bugs.md`).

## Test plan
- [x] 836 tests pass (was 834; +2 covering: not-allowed-user rejected
      with `not authorised` comment; sender failing org-membership
      check rejected with `must be a member` comment)
- [x] eslint clean
- [ ] After deploy: open a `repo-request`-labelled issue from a
      non-allowed-list account in `pulseengine/repo-requests` (when
      `controller_repo.enabled: true`). Bot should refuse rather than
      provision.

## Risk & rollout
- Risk: low. Default behaviour for `allowed_command_users: []` is
  unchanged (still requires org membership, which existing maintainers
  already have). Anyone who'd previously rely on "no allowlist + no
  org check" must now be in the org — which is a sensible default for
  issue-form provisioning.
- Rollout: self-update on merge.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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