reported via email on 4 June 2026 - no response:
I found a critical authorization flaw in the current Peppermint codebase (commit ba6e217) that renders the entire permission system non-functional in all default deployments.
The requirePermission middleware in apps/api/src/lib/roles.ts only enforces role-based permissions when the database flag roles_active is set to true. The schema declares this column with @default(false), and the migration that introduced it also seeds it as false. Because the shipped docker-compose.yml does not set this flag, every fresh installation starts with roles_active = false. When that condition is false the middleware unconditionally returns without checking any permissions, so every route it guards becomes accessible to any authenticated user.
This means a regular user account (role: user) can read, edit, and delete any ticket belonging to any other user or client, delete any comment, reassign tickets, close or hide tickets, and create webhooks that point to arbitrary URLs including internal network addresses. On a ticket creation event the webhook fires as an outbound HTTP POST with no URL filtering, which provides a server-side request forgery primitive for reaching internal services.
In addition to the middleware bypass, several administrative configuration routes in apps/api/src/controllers/config.ts carry no authentication check of any kind. Any authenticated user can overwrite the OIDC issuer and redirect URI, overwrite SMTP credentials, or disable SSO entirely. Replacing the OIDC provider with an attacker-controlled one allows the attacker to intercept an admin's authorization code the next time the admin attempts an SSO login, achieving account takeover.
All findings were live-validated on a fresh docker-compose deployment.
Remediation: Set roles_active to true by default in the schema, or replace the conditional with an unconditional permission check. Apply an isAdmin guard or the requirePermission preHandler to all config mutation routes.
reported via email on 4 June 2026 - no response:
I found a critical authorization flaw in the current Peppermint codebase (commit ba6e217) that renders the entire permission system non-functional in all default deployments.
The requirePermission middleware in apps/api/src/lib/roles.ts only enforces role-based permissions when the database flag roles_active is set to true. The schema declares this column with @default(false), and the migration that introduced it also seeds it as false. Because the shipped docker-compose.yml does not set this flag, every fresh installation starts with roles_active = false. When that condition is false the middleware unconditionally returns without checking any permissions, so every route it guards becomes accessible to any authenticated user.
This means a regular user account (role: user) can read, edit, and delete any ticket belonging to any other user or client, delete any comment, reassign tickets, close or hide tickets, and create webhooks that point to arbitrary URLs including internal network addresses. On a ticket creation event the webhook fires as an outbound HTTP POST with no URL filtering, which provides a server-side request forgery primitive for reaching internal services.
In addition to the middleware bypass, several administrative configuration routes in apps/api/src/controllers/config.ts carry no authentication check of any kind. Any authenticated user can overwrite the OIDC issuer and redirect URI, overwrite SMTP credentials, or disable SSO entirely. Replacing the OIDC provider with an attacker-controlled one allows the attacker to intercept an admin's authorization code the next time the admin attempts an SSO login, achieving account takeover.
All findings were live-validated on a fresh docker-compose deployment.
Remediation: Set roles_active to true by default in the schema, or replace the conditional with an unconditional permission check. Apply an isAdmin guard or the requirePermission preHandler to all config mutation routes.