Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ jobs:
TF_VAR_modal_api_secret: ${{ secrets.MODAL_API_SECRET }}
TF_VAR_allowed_users: ${{ secrets.ALLOWED_USERS }}
TF_VAR_allowed_email_domains: ${{ secrets.ALLOWED_EMAIL_DOMAINS }}
TF_VAR_allowed_github_orgs: ${{ secrets.ALLOWED_GITHUB_ORGS }}
TF_VAR_deployment_name: ${{ secrets.DEPLOYMENT_NAME }}
TF_VAR_enable_github_bot: "${{ secrets.ENABLE_GITHUB_BOT || 'false' }}"
TF_VAR_github_webhook_secret: ${{ secrets.GH_WEBHOOK_SECRET }}
Expand Down Expand Up @@ -335,6 +336,7 @@ jobs:
TF_VAR_modal_api_secret: ${{ secrets.MODAL_API_SECRET }}
TF_VAR_allowed_users: ${{ secrets.ALLOWED_USERS }}
TF_VAR_allowed_email_domains: ${{ secrets.ALLOWED_EMAIL_DOMAINS }}
TF_VAR_allowed_github_orgs: ${{ secrets.ALLOWED_GITHUB_ORGS }}
TF_VAR_deployment_name: ${{ secrets.DEPLOYMENT_NAME }}
TF_VAR_enable_github_bot: "${{ secrets.ENABLE_GITHUB_BOT || 'false' }}"
TF_VAR_github_webhook_secret: ${{ secrets.GH_WEBHOOK_SECRET }}
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ built for internal use where all employees are trusted and have access to compan
web interface
2. **Install GitHub App only on intended repositories** - The App's installation scope defines what
the system can access
3. **Use GitHub's repository selection** - When installing the App, select specific repositories
3. **Restrict sign-in** - Configure allowed GitHub users, email domains, or active GitHub
organization membership (`ALLOWED_GITHUB_ORGS`)
4. **Use GitHub's repository selection** - When installing the App, select specific repositories
rather than "All repositories"

## Architecture
Expand Down
36 changes: 23 additions & 13 deletions docs/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,21 +215,25 @@ access.
- Issues: **Read & Write** _(required if enabling GitHub bot)_
- Pull requests: **Read & Write**
- Metadata: **Read-only**
6. Click **"Create GitHub App"**
7. Note the **App ID** and **Client ID** (top of page)
8. Under **"Client secrets"**, click **"Generate a new client secret"** and note the **Client
6. If using `ALLOWED_GITHUB_ORGS`/`allowed_github_orgs`, set **Organization permissions**:
- Members: **Read-only**
- For existing GitHub Apps, republish the permission change and request/approve installation
updates before testing org membership sign-in.
7. Click **"Create GitHub App"**
8. Note the **App ID** and **Client ID** (top of page)
9. Under **"Client secrets"**, click **"Generate a new client secret"** and note the **Client
Secret**
9. Scroll down to **"Private keys"** and click **"Generate a private key"** (downloads a .pem file)
10. **Convert the key to PKCS#8 format** (required for Cloudflare Workers):
10. Scroll down to **"Private keys"** and click **"Generate a private key"** (downloads a .pem file)
11. **Convert the key to PKCS#8 format** (required for Cloudflare Workers):
```bash
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt \
-in ~/Downloads/your-app-name.*.private-key.pem \
-out private-key-pkcs8.pem
```
11. **Install the app** on your account/organization:
12. **Install the app** on your account/organization:
- Click "Install App" in the sidebar
- Select the repositories you want Open-Inspect to access
12. Note the **Installation ID** from the URL after installing:
13. Note the **Installation ID** from the URL after installing:
```
https://github.com/settings/installations/INSTALLATION_ID
```
Expand Down Expand Up @@ -416,15 +420,20 @@ enable_service_bindings = false
# Access Control (set at least one allowlist for production)
allowed_users = "your-github-username" # Comma-separated GitHub usernames, or empty
allowed_email_domains = "" # Comma-separated domains (e.g., "example.com,corp.io")
allowed_github_orgs = "" # Comma-separated orgs whose active members can sign in

# Explicitly opt into open access only if you want any authenticated GitHub user
# to be able to sign in when both allowlists are empty.
# to be able to sign in when all allowlists are empty.
unsafe_allow_all_users = false
```

> **Note**: Review `allowed_users` and `allowed_email_domains` carefully - these control who can
> sign in. Terraform now fails if both are empty unless you explicitly set
> `unsafe_allow_all_users = true`.
> **Note**: Review `allowed_users`, `allowed_email_domains`, and `allowed_github_orgs` carefully -
> these control who can sign in. Terraform now fails if all are empty unless you explicitly set
> `unsafe_allow_all_users = true`. **Allowlists use OR semantics**: matching any configured
> username, email domain, or active GitHub org membership grants access. `allowed_github_orgs`
> checks membership at sign-in only with the signing-in user's OAuth token; existing sessions last
> until session expiry. The `read:org` OAuth scope is requested only when org access is configured.
> GitHub Apps using org access need Organization permissions: Members read-only.

---

Expand Down Expand Up @@ -683,8 +692,9 @@ Go to your fork's Settings → Secrets and variables → Actions, and add:
| `INTERNAL_CALLBACK_SECRET` | Generated callback secret |
| `MODAL_API_SECRET` | Generated Modal API secret |
| `NEXTAUTH_SECRET` | Generated NextAuth secret |
| `ALLOWED_USERS` | Comma-separated GitHub usernames (or empty for all users) |
| `ALLOWED_EMAIL_DOMAINS` | Comma-separated email domains (or empty for all domains) |
| `ALLOWED_USERS` | Comma-separated GitHub usernames allowed to sign in |
| `ALLOWED_EMAIL_DOMAINS` | Comma-separated email domains allowed to sign in |
| `ALLOWED_GITHUB_ORGS` | Comma-separated GitHub orgs whose active members can sign in |
| `ENABLE_GITHUB_BOT` | `true` to deploy GitHub bot worker (or empty to skip) |
| `GH_WEBHOOK_SECRET` | GitHub webhook secret (required if GitHub bot enabled) |
| `GH_BOT_USERNAME` | GitHub App bot username, e.g., `my-app[bot]` (required if GitHub bot enabled) |
Expand Down
11 changes: 10 additions & 1 deletion docs/SETUP_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,14 @@ NEXT_PUBLIC_WS_URL=wss://open-inspect-control-plane-<name>.<subdomain>.workers.d
INTERNAL_CALLBACK_SECRET=your_shared_secret

# Optional access control
# Allowlists are OR-based: matching any configured user, email domain, or GitHub org grants access.
# ALLOWED_GITHUB_ORGS requests read:org only when set, then checks active org membership
# with the signing-in user's OAuth token. Existing sessions last until session expiry.
# Requires GitHub App Organization permissions: Members read-only.
ALLOWED_USERS=
ALLOWED_EMAIL_DOMAINS=
ALLOWED_GITHUB_ORGS=
UNSAFE_ALLOW_ALL_USERS=false

# Optional whitelabel branding (defaults shown). NEXT_PUBLIC_* vars are
# inlined into the client bundle at build time — restart `npm run dev`
Expand Down Expand Up @@ -212,7 +218,10 @@ Your GitHub callback URL does not exactly match the running app URL.

### Access denied after sign-in

Check `ALLOWED_USERS` and `ALLOWED_EMAIL_DOMAINS` in `packages/web/.env.local`.
Check `ALLOWED_USERS`, `ALLOWED_EMAIL_DOMAINS`, and `ALLOWED_GITHUB_ORGS` in
`packages/web/.env.local`. If `ALLOWED_GITHUB_ORGS` is set, make sure your GitHub App has
Organization permissions: Members read-only and that the updated permission was republished and
approved for the installation.

### Web can load, but session APIs return 401

Expand Down
8 changes: 7 additions & 1 deletion packages/web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ NEXT_PUBLIC_WS_URL=wss://open-inspect-control-plane.YOUR-ACCOUNT.workers.dev
# Generate with: openssl rand -base64 32
INTERNAL_CALLBACK_SECRET=

# Access Control (comma-separated, leave empty to allow all GitHub users)
# Access Control (comma-separated; configure at least one allowlist unless UNSAFE_ALLOW_ALL_USERS=true)
# **OR-based**: matching any configured user, email domain, or GitHub org grants access.
# ALLOWED_GITHUB_ORGS requests read:org only when set, then checks active org membership
# with the signing-in user's OAuth token. Existing sessions last until session expiry.
# Requires GitHub App Organization permissions: Members read-only.
ALLOWED_EMAIL_DOMAINS=
ALLOWED_USERS=
ALLOWED_GITHUB_ORGS=
UNSAFE_ALLOW_ALL_USERS=false
16 changes: 11 additions & 5 deletions packages/web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,23 @@ NEXTAUTH_SECRET=your_random_secret # Generate: openssl rand -base64 32
# Access Control
ALLOWED_USERS=username1,username2 # Comma-separated GitHub usernames
ALLOWED_EMAIL_DOMAINS=example.com,corp.io # Comma-separated email domains
UNSAFE_ALLOW_ALL_USERS=false # Set true to explicitly allow all users when both lists are empty
ALLOWED_GITHUB_ORGS=acme,umbrella # Comma-separated GitHub orgs with active members allowed
UNSAFE_ALLOW_ALL_USERS=false # Set true to explicitly allow all users when all lists are empty

# Control Plane
CONTROL_PLANE_URL=http://localhost:8787
NEXT_PUBLIC_WS_URL=ws://localhost:8787
```

> **Access Control**: If both `ALLOWED_USERS` and `ALLOWED_EMAIL_DOMAINS` are empty, sign-in is
> denied unless `UNSAFE_ALLOW_ALL_USERS=true`. For Terraform-managed production deploys, Terraform
> also fails validation unless you set at least one allowlist or explicitly opt in with
> `unsafe_allow_all_users = true`.
> **Access Control**: If `ALLOWED_USERS`, `ALLOWED_EMAIL_DOMAINS`, and `ALLOWED_GITHUB_ORGS` are all
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

[Automated Review] M4 (MEDIUM) — OR semantics is a footgun; needs a bold callout

The current phrasing ("If ALLOWED_USERS, ALLOWED_EMAIL_DOMAINS, and ALLOWED_GITHUB_ORGS are all empty, sign-in is denied unless UNSAFE_ALLOW_ALL_USERS=true") doesn't make the union semantics obvious.

A reasonable operator instinct is AND: they set ALLOWED_EMAIL_DOMAINS=acme.com AND ALLOWED_GITHUB_ORGS=acme expecting "must be in the org AND on the corp domain." In reality they get a union — a GitHub account with an unverified someone@acme.com primary email (see M5) passes via the weaker email check and never reaches the server-enforced org check.

Suggested addition: a bold callout near this block: "⚠️ These lists are OR-combined. Configuring multiple allowlists widens access, not narrows it. Each list adds an independent way to be granted access." Same callout belongs in packages/web/.env.example and docs/GETTING_STARTED.md.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 07a834b.

The docs now explicitly call out that allowlists use OR semantics: matching any configured GitHub
username, email domain, or active GitHub org membership grants access. This is documented in:

  • docs/GETTING_STARTED.md
  • packages/web/README.md
  • packages/web/.env.example
  • docs/SETUP_GUIDE.md
  • terraform/environments/production/terraform.tfvars.example

> empty, sign-in is denied unless `UNSAFE_ALLOW_ALL_USERS=true`. For Terraform-managed production
> deploys, Terraform also fails validation unless you set at least one allowlist or explicitly opt
> in with `unsafe_allow_all_users = true`. **Allowlists use OR semantics**: matching any configured
> username, email domain, or active GitHub org membership grants access. `ALLOWED_GITHUB_ORGS` is
> checked at sign-in with the signing-in user's OAuth token; existing sessions last until session
> expiry. The `read:org` OAuth scope is requested only when `ALLOWED_GITHUB_ORGS` is configured.
> GitHub Apps using org access need Organization permissions: Members read-only; existing GitHub
> Apps must republish/request approval after that permission changes.

### Development

Expand Down
Loading