Dosu LogoDosu Logo
Ask
Join our Discord
Organization avatar
HashHivePublic
EvilBit Labs LLC
DocumentsHashHive
feat-betterauth-project-selection-rbac
feat-betterauth-project-selection-rbac
Type
External
Status
Published
Created
Jul 18, 2026
Updated
Jul 18, 2026
Source
docs/residual-review-findings/feat-betterauth-project-selection-rbac.md

Residual Review Findings — Issue #159#

Branch: feat/betterauth-project-selection-rbac
HEAD at filing: a983106
Source review run: /tmp/compound-engineering/ce-code-review/20260526-033221-38a7ed0d/
Reviewers dispatched: 12 (correctness, security, testing, maintainability, project-standards, api-contract, data-migration, reliability, adversarial, ce-agent-native-reviewer, ce-learnings-researcher, ce-deployment-verification-agent)
Mode: autofix
Plan: docs/plans/2026-05-26-001-feat-betterauth-project-selection-rbac-plan.md (explicit)

This file is the durable record of review findings that were NOT addressed in the same PR. Created per superpowers/lfg step 5 fallback path because no PR existed at filing time. When the PR is created (LFG step 7), its body will link here.


Applied in-branch (no follow-up needed)#

#TypeFileDescription
1safe_autopackages/openapi/dashboard-api.yamlAdded 401 AuthRequired to /projects/select, 500 API_KEY_READ_FAILED to GET /me/api-key, 400 ValidationFailed to PATCH /projects/{projectId}/members/{userId}, and enumerated all three INTERNAL_ERROR failure modes (incl. setUserLastProjectId failure) on POST /projects/select 500.
2safe_autopackages/openapi/dashboard-api.yamlAdded 4 missing fields to GET /projects response schema (description, settings, createdAt required, description/settings nullable). Closes api-contract AC-001.
3safe_autopackages/shared/src/types/index.tsCorrected SessionUser docstring — the previous "AppEnv mirrors the shape" claim was technically false (selectedProjectId vs projectId field names).
4safe_autopackages/backend/src/middleware/auth.tscoerceRoles exported + emits a warning when input had values but all were dropped (data drift signal).
5safe_autopackages/backend/src/middleware/auth.tsOperator-visible warn log when session.session.projectId surfaces as non-number type.
6safe_autopackages/backend/src/middleware/api-key.tsReplaced asymmetric row.roles as UserRole[] cast with coerceRoles(row.roles, row.id) — both auth surfaces now apply the same UserRole allowlist. Closes security/adversarial/maintainability 3-reviewer corroboration.
7safe_autopackages/backend/src/routes/dashboard/auth.tsWrapped GET /me's getUserWithProjects in try/catch. Closes reliability M1.
8safe_autopackages/frontend/src/stores/auth.tsfetchProjects no longer silently swallows errors; logs via console.error. Closes reliability M2.
9manual fixpackages/backend/src/services/projects.tsremoveUserFromProject now invalidates ba_sessions.project_id AND users.last_project_id in the same transaction. Closes adversarial adv-001 (P1).

All applied fixes verified with just check + just test (499 backend + 327 frontend tests green).


Residual Review Findings#

The autofix pass applied 9 changes; the items below were judged too high-touch for the security-flip PR or require cross-team decisions, and are recorded here so the PR review conversation can route them.

P1 — currentUser.projectId vs SessionUser.selectedProjectId field-name divergence#

Reviewer corroboration: maintainability M1 (P1) + api-contract AC-005 (low).
File: packages/backend/src/types.ts:18.

AppEnv['Variables']['currentUser'] uses field name projectId. The shared sessionUserSchema and meResponseSchema.selectedProjectId use selectedProjectId. The autofix corrected the misleading docstring; the deeper rename would touch ~60 destructures + 20 route handlers. Out of scope for the security PR. Recommend a follow-up PR refactor(types): rename currentUser.projectId → selectedProjectId for shape parity with SessionUser.

Why this didn't land here: Mechanical rename across every dashboard route handler, with no behavior change. Easier to review in a focused refactor PR; keeps this diff's security-flip story clean.

P1 — R7 tier matrix not applied across dashboard routes#

Reviewer: api-contract AC-005 (high).
Files: packages/backend/src/routes/dashboard/{campaigns,resources,attack-templates,agents}.ts.

Plan #159 R7 defines a tier matrix gating destructive ops on requireRole('admin', 'operator') (global) and creates on requireRole('admin', 'operator', 'analyst'). Only routes/dashboard/crackers.ts mounts the new global requireRole. Other dashboard routes still gate per-project only — a user with global tier analyst who is a per-project admin can still delete campaigns/resources.

Recommended fix shape: Per-route audit against the matrix. Destructive endpoints (campaign delete, run/stop/pause/resume/cancel, resource delete, attack-template delete) get requireRole('admin', 'operator') alongside the existing requireMembershipRole(...). Test fixtures need session mocks with non-admin global roles to cover the operator-allowed and analyst-rejected paths.

Why this didn't land here: Touches 5+ route files, ~10 endpoints, and requires updating 5+ test files' session fixtures to seed non-admin roles. The U5 commit description does not claim full matrix rollout; it lands the split + the crackers example. Recommend a follow-up PR feat(rbac): apply R7 tier matrix to dashboard campaigns/resources/attack-templates routes.

P2 — Migration backfill WHERE roles = ARRAY['analyst'] is not idempotent#

Reviewer corroboration: api-contract AC-006 + maintainability M8 + testing-002.
File: packages/shared/src/db/migrations/0010_salty_blazing_skull.sql:10.

UPDATE users SET roles = ARRAY['admin'] WHERE roles = ARRAY['analyst'] clobbers legitimate analyst users if ever replayed against a populated DB. Drizzle's migrator won't replay applied migrations, but db:push against a snapshot mid-deploy or a manual re-run could.

Why this didn't land here: The migration is already committed and would be applied to prod. Editing committed migrations post-commit is risky. Mitigation belongs in deployment procedures (see the ce-deployment-verification-agent's checklist at the run artifact) — and in a future migration that scopes the backfill by created_at if the pattern recurs.

P2 — Test fixture duplication across 6 dashboard test files#

Reviewer: maintainability M4.
Files: packages/backend/tests/unit/{dashboard-agents-routes,dashboard-resources-routes,dashboard-campaigns-routes,dashboard-api-key-routes,crackers-routes,attack-templates}.test.ts.

Six tests duplicate the BetterAuth getSession + auth-service mock block. The U7 commit (016051b) AND the U6 followup (7f87bc1) both forced shotgun edits across all six to add new service exports. A shared tests/helpers/mock-session.ts factory would consolidate.

Why this didn't land here: A test-infra refactor that touches every dashboard route test file is its own PR. Recommend refactor(tests): extract shared BetterAuth + auth-service mock factory.

P2 — Integration tests promised in plan §5.1 substituted with unit-level mocks#

Reviewer: testing-003 (medium).
Files (not created): tests/integration/dashboard-project-scope.test.ts, tests/integration/auth-me-selected-project.test.ts.

Plan §5.1 promised integration tests against the real BetterAuth + Postgres adapter. Implementation landed unit-level mocks at tests/unit/routes/ instead. BetterAuth's databaseHooks wiring is therefore not exercised end-to-end. The session-invalidation-on-revocation fix (applied here as adv-001) ALSO needs an integration test — the planned unit-level test ran into mock.module isolation issues and was dropped during the autofix pass.

Why this didn't land here: Integration tests against the BetterAuth runtime require lifting Postgres in the test environment, which is its own scaffolding work. Recommend adding the integration tests in the same PR that ships the R7 tier matrix rollout (P1 #2 above) so both gain coverage together.

P2 — Login auto-select test doesn't exercise the new server-priority branch#

Reviewer: testing-001.
File: packages/frontend/tests/pages/login.test.tsx:97.

syncSelectedProject was extended in U7 to prefer the server's selectedProjectId over legacy single-project fallback. The login test passes selectedProjectId: null via the fixture default, so it exercises ONLY the legacy fallback — never the new branch.

Why this didn't land here: Adding the test is straightforward but the test infra for mockMeResponse would need an explicit selectedProjectId parameter wired through. Quick follow-up.

Advisory (report only)#

  • Multi-tab /projects/select last-write-wins (adversarial adv-004) — accepted at HashHive's 1-3 concurrent user scale.
  • Migration is one-way operationally (adversarial adv-005) — documented in the ce-deployment-verification-agent's checklist (run artifact); rollback requires deploying previous app version first.
  • Control API parity for global RBAC (ce-agent-native-reviewer) — requireApiKey populates currentUser.roles (via shared coerceRoles after the autofix), but no control route mounts requireRole. Cracker management, project member management, hash list creation remain dashboard-only. Recommend a follow-up feat(control-api): admin-tier parity for cracker + member + hash-list management.
  • Backfill makes every existing user a global admin (data-migration + deployment-verification) — documented in migration comment + plan §6.4 + deployment checklist. Operator action post-deploy: downgrade non-admin staff via direct SQL.

Verdict#

Ready with fixes. No P0 blockers. The 9 in-branch fixes close the docs / observability / asymmetric-narrowing / session-invalidation gaps. The 6 residual items break into 2 P1 follow-ups (currentUser rename + tier matrix rollout), 4 P2 cleanups (migration idempotency, fixture deduplication, integration tests, login test branch), and 4 advisory items.

Plan-requirements coverage is met per the explicit-plan check — all 12 R-IDs and 7 U-IDs have corresponding work in the diff. The P1 residuals are scope expansions discovered during review, not regressions on planned work.

Documents
.impeccable
0001-agent-enrollment-two-token-exchange
0002-docker-supervision-over-pm2
0003-bind-agent-to-enrollment-token
0004-agent-api-revisable-until-1.0
0005-version-public-api-after-1.0
0006-seaweedfs-over-minio
0007-three-api-surfaces
0008-betterauth-over-handrolled-jwt
0009-postgres-task-store-bullmq-orchestration
0010-schema-first-drizzle-zod
0011-pull-advisory-notify-task-distribution
0012-in-memory-websocket-v1-redis-deferred
0013-route-as-spec-hono-zod-openapi
0014-turborepo-bun-over-nx
0015-append-only-telemetry-timescaledb-rrd
0016-server-fanout-bus
0017-adaptive-task-sizing-lease
0018-real-db-integration-test-lane
0019-campaign-archiving-immutable-lifecycle
155-ac-traceability-matrix
155-task-distribution-assignment-spec
156-ac-traceability-matrix
156-object-storage-file-management-spec
160-login-project-selection-ui
2026-05-25-websocket-realtime-infrastructure-requirements
2026-05-29-dashboard-stats-and-read-endpoint-contract-requirements
2026-05-31-openapi-auto-generation-migration-requirements
2026-06-08-results-analysis-export-ui-requirements
2026-06-19-task-distribution-architecture
2026-06-20-campaign-archiving-requirements
2026-06-30-hash-export-import-search-requirements
227-spa-deep-link-refresh-redirect-spec
94-p0-cracker-binary-management-agent-auto-update
96-keyspace-task-distribution-spec
97-task-preemption-spec
99-attack-complexity-state-machine-spec
AGENTS
AGENTS
AI_POLICY
AI_POLICY
ARCHITECTURE
ARCHITECTURE
Agent_Authentication_&_Authorization
Agent_Heartbeat_&_Error_Handling
Agent_List_&_Detail_UI
Base Model Schema System
BullMQ_Queue_Architecture_&_Redis_Integration
CLAUDE
CONTRIBUTING
CONTRIBUTING
Campaign_Creation_Wizard_UI
Campaign_List_&_Detail_UI
Campaign_Orchestration_API
Code Quality Automation with Husky and Kiro Hooks
Core_Flows__HashHive_User_Journeys
Dashboard_&_Real-Time_Monitoring_UI
Dashboard_Stats_API_Endpoint_(GET__api_v1_dashboard_stats)
Development Workflow and Tooling Integration
ESLint and Formatting Configuration
Epic_Brief__HashHive_-_Modern_Distributed_Password_Cracking_Platform
GOTCHAS
GOTCHAS
Health Check Endpoint Enhancements
Login_&_Project_Selection_UI
MERN_proposal
Mixed Hash Type Detection & Automatic Campaign Splitting (#202)
MongoDB Connection Management
Object_Storage_&_File_Management
Project_Selection_&_User_Authentication_API
README
README
README
README
README
README
Real-Time_Events_&_WebSocket_Infrastructure
Resource_Management_API
Resource_Management_UI
Results_API_&_CSV_Export_(Dashboard_Backend)
Results_Analysis_&_Export_UI
SECURITY
SECURITY
STRATEGY
Task_Distribution_&_Assignment
Tech_Plan__HashHive_Architecture
Testing Strategy for Database and Models
advanced_task_scheduler
agent
agent-resource-protocol
agent-server-collaboration-vision
agent-token-rotation
agent_notes
agent_sync_extensions
agent_v1_contract_violations
allow_hybrid_hash_lists
attack
attack_notes
authentication_cleanup
back-compat-credential-format-migration
barrel-plus-subdir-service-split
brute_force_attack_editor
bullmq-jobid-dedup-requires-eviction
bun-test-mock-module-import-order
campaign_list_view
campaign_notes
campaigns
ci_check.prompt
code_review.prompt
contract-test-mocks-mirror-service-not-schema
copilot-instructions
core_algorithm_implementation_guide
crackable_uploads_plan
dashboard-read-endpoint-contract
dashboard-ux
default_config_suggestions
development
development
feat-agent-list-detail-ui
feat-betterauth-project-selection-rbac
feat-campaign-list-detail-ui
feat-masklist-keyspace
form-submit-payload-null-checks-2026-05-19
full_testing_architecture
getbytext-collision-waitfor-timeout
hard_password_attack_strategies
hash_hive_gap_analysis
health_status_screen
implementation_plan
learned_rules_parser_plan
llmstxt.prompt
markov_autogen_plan
new_dictionary_attack_editor
new_mask_attack_editor
openldap-testcontainer-bootstrap-race
performance_tuning.prompt
phase-1-core-infrastructure
phase-2-api-implementation
phase-2-api-implementation-part-1
phase-2-api-implementation-part-2
phase-2-api-implementation-part-3
phase-2b-resource-management
phase-2c-refactor-cleanup
phase-3-e2e-test-coverage-plan
phase-3-step-1
phase-3-step-2
phase-3-step-3
phase-3-step-4
phase-3-step-5
phase-3-web-ui-foundation
phase-4-containerization-deployment
phase-5-task-distribution
phase-6-monitoring-testing-documentation
playwright-shared-seed-user-last-project-id-flake
playwright-visual-baselines
pre-prod-wire-rename-and-schema-version-bump
previous_password_dictionary_attack_editor
projection-narrows-zrecord-wire-shape
psycopg_migration
radix-roving-focus-preventdefault-breaks-keyboard-nav
reactflow-position-by-array-index-2026-05-19
rule_explanation
salvage_templates
security_hardening.prompt
shadcn-radix-non-destructive-add-on-tailwind-v4
shared-zod-openapi-wire-contract-mirror-2026-05-25
spa_to_ssr
summary
template
test-coverage-plan
testcontainers_minio_support
testing
testing
user_flows_notes
v2-upgrade-overview
v2_agent_api_removal
work_next_task.prompt