Tighten Admin Validations + IDOR Prevention#1641
Conversation
…ck token at-rest End-to-end audit of every Auth0-touching authorization pathway. Twelve verified findings shipped; agent-reported false positives (intentional IDOR-safe SetCorpusVisibility, AnnotationImagesView, moderation mutations, moderation_metrics, etc.) verified in source and dropped. - F1: AUTH0_SUPERUSER_SUB_ALLOWLIST gates is_superuser elevation in sync_admin_claims_from_payload regardless of JWT claim, blocking the user_metadata-sourced privilege escalation path. New users.W001 system check warns on empty allowlist when USE_AUTH0=True. - F2: ADMIN_CLAIMS_CACHE_TTL 300s -> 30s, tightening the privilege retention window after Auth0 demotion. - F3+F4: AddRelationship now uses Annotation/Corpus visible_to_user with count comparison and a single unified not-found message; broad except no longer surfaces ORM text. - F5+F6: CreateMetadataColumn and UpdateMetadataColumn use visible_to_user + unified not-found-or-no-permission message. - F7: Datacell mutations catch DoesNotExist explicitly and stop echoing ORM exception text. - F9: Bounded JWKS stale-cache fallback (1h grace past TTL); fails closed beyond the window so a key compromise + Auth0 outage cannot let the old key keep verifying tokens indefinitely. - F10: Analysis.callback_token is now stored as SHA-256 hex (callback_token_hash). rotate_callback_token() returns a fresh plaintext at submit time; verify_callback_token() does the constant-time check on incoming requests. Migration backfills existing UUID tokens by hash so in-flight analyzers continue to authenticate. Submit log redacted. - F8+F11: Documentation-only. Step 7 walkthrough for the allowlist, env-var rows for AUTH0_SUPERUSER_SUB_ALLOWLIST + AUTH0_CREATE_NEW_USERS, app_metadata-only requirement called out in danger callout, inline comment that User.email is informational (not an identity field). Operator action required before deploy: populate AUTH0_SUPERUSER_SUB_ALLOWLIST with the Auth0 sub of every user who must retain Django is_superuser. Existing superusers whose subs are not in the allowlist will be demoted within ~30s of their next API call.
| # protects against misconfigured tenant Actions that source admin claims | ||
| # from user-writable ``user_metadata``. Existing superusers whose subs are | ||
| # not added here are demoted on next sync; populate this BEFORE deploy. | ||
| AUTH0_SUPERUSER_SUB_ALLOWLIST = env.list( |
Code Review: Security Hardening — Auth0 Permissioning Audit & Callback Token HashingThis is a well-scoped, defence-in-depth security PR. The changes are correct in intent and largely well-implemented. Below is a detailed breakdown. Overall AssessmentPositive
Issues & SuggestionsMedium — Document permission gap in
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Code Review: Tighten Admin Validations + IDOR PreventionThis is a well-structured, defense-in-depth security PR. The changes are clearly motivated, consistently applied, and come with good test coverage. Below is a thorough review. OverviewThe PR addresses three distinct security layers:
All three areas contain real security improvements and the changes are generally correct. The following notes cover bugs, edge cases, and minor style issues. Findings1.
|
Summary
Comprehensive security audit of Auth0 integration and analyzer callback handling. Twelve real fixes across three layers: defense-in-depth allowlist for
is_superuserJWT elevation, IDOR-safe error messages in GraphQL mutations, and SHA-256 hashing of analyzer callback tokens to prevent credential leakage via database reads.Key Changes
Auth0 Privilege Escalation Prevention
is_superuser: AddedAUTH0_SUPERUSER_SUB_ALLOWLISTenvironment variable. JWT claim sync now refuses to elevateis_superusertoTrueunless the user's Auth0sub(Djangousername) appears in the allowlist, regardless of verified token claims. Blocks misconfigured tenants sourcing admin claims from user-writableuser_metadata.ADMIN_CLAIMS_CACHE_TTLfrom 300 to 30 seconds for tighter privilege revocation SLA.opencontractserver/users/checks.pyto warn at startup when Auth0 is enabled but allowlist is empty.docs/configuration/authentication.mdabout sourcing claims fromapp_metadata(notuser_metadata) and detailed allowlist setup instructions.IDOR-Safe GraphQL Mutations
AddRelationship,CreateMetadataColumn, andUpdateMetadataColumnmutations now return identical error messages for both "does not exist" and "no permission" cases, preventing enumeration attacks.visible_to_user: Source and target annotations filtered throughAnnotation.objects.visible_to_user(user)with count validation to catch both missing and unauthorized IDs without echoing them back.visible_to_user()querysets; non-existent and inaccessible resources collapse into the same error branch.logger.exception()but return generic "Error creating X" messages to clients.Analyzer Callback Token Security
callback_tokenfield withcallback_token_hash(CharField, max_length=64). Database never stores plaintext; a DB read alone cannot let an attacker forge analyzer callbacks.Analysis.rotate_callback_token()generates freshsecrets.token_urlsafe(32)plaintext on each submission, invalidating any in-flight callbacks bound to previous tokens.Analysis.verify_callback_token(candidate)useshmac.compare_digest()to prevent timing leaks.Supporting Changes
opencontractserver/analyzer/views.pyto callverify_callback_token()instead of direct string comparison.test_analyzers.pyandtest_security_hardening.pyto mint plaintext tokens viarotate_callback_token()before submission.AUTH0_CREATE_NEW_USERSenvironment variable (defaultTrue) to control auto-provisioning of new Auth0 users.User.emailas informational (nounique=Trueconstraint);User.username(Auth0sub) is the only identity field._JWKS_STALE_GRACE = 3600seconds) to bound how long a compromised keyhttps://claude.ai/code/session_01DEQ8YPfGpzKHr1VviKjdpX