Skip to content

feat(eligibility): superAdmin-only writes + v4 upgrade script#167

Merged
hudsonhrh merged 1 commit into
mainfrom
hudsonhrh/decentral-park-org-config-audit
May 26, 2026
Merged

feat(eligibility): superAdmin-only writes + v4 upgrade script#167
hudsonhrh merged 1 commit into
mainfrom
hudsonhrh/decentral-park-org-config-audit

Conversation

@hudsonhrh
Copy link
Copy Markdown
Member

Summary

  • Replaces onlyHatAdmin(hatId) with onlySuperAdmin on every EligibilityModule write path so only the org's Executor can mutate eligibility — Hats hierarchy now only matters for vouching (via combineWithHierarchy), closing the previous vouching-bypass where any hat-admin could force-add or force-revoke wearers directly. Retires the unreachable NotAuthorizedAdmin error and tightens hasAdminRights to only return true for the superAdmin.
  • Adds 12 regression tests in a new SUPER-ADMIN GATING section covering every gated function against a hierarchical admin, plus the end-to-end "hierarchy admin cannot bypass vouching via direct Hats.mintHat" test and combineWithHierarchy on/off vouching cases. Rewrote the pre-existing hierarchy-admin tests to assert NotSuperAdmin reverts and route success cases through setup.exec.
  • New script/upgrades/UpgradeEligibilitySuperAdminLockdown.s.sol with the standard 3-step cross-chain rollout (Step1 Gnosis DD deploy → Step2 Arbitrum DD deploy + Hyperlane beacon dispatch → Step3 Gnosis verify) at v4 — probed FREE on both registry + CREATE2 surfaces on both chains, deterministic addr 0x881330E39EbD920e0406D066cf775168c3726239.
  • DryRun_GnosisUpgrade runs end-to-end against KUBI's live module on a Gnosis fork under FOUNDRY_PROFILE=production — all 9 assertions pass (storage preservation, caleb-as-hierarchical-admin blocked with NotSuperAdmin, KUBI_EXECUTOR round-trips (true,true)→(false,false)→cleared, hasAdminRights tightened correctly).
  • No storage layout changes — safe upgrade for KUBI / Test6 / Poa / any other org on the EligibilityModule beacon.

Test plan

  • forge build (default profile)
  • forge fmt
  • forge test --match-contract DeployerTest --match-test "testHierarchyAdmin|testUnaffiliatedAddress" — 12/12 PASS
  • Regression sweep: testEligibilityModuleAdminHatSystem, testExecutiveGivesHatsToTwoPeopleThenTurnsOffOne, testVouchingSystemHybridMode, testRegisterHatCreation*, testOption1SingleTransactionCreateAndMint — 22/22 PASS
  • forge test --match-contract BatchFunctionsTest — 36/36 PASS
  • FOUNDRY_PROFILE=production forge script .../UpgradeEligibilitySuperAdminLockdown.s.sol:DryRun_GnosisUpgrade --fork-url gnosis — ALL DRY-RUN CHECKS PASSED
  • After review: broadcast Step1 (Gnosis) → Step2 (Arbitrum, ~0.005 ETH Hyperlane fee) → wait ~5 min → Step3 verify

🤖 Generated with Claude Code

Replaces `onlyHatAdmin(hatId)` with `onlySuperAdmin` on every EligibilityModule
write path (setWearerEligibility, setDefaultEligibility, clearWearerEligibility,
setBulkWearerEligibility, batchSetWearerEligibility, createHatWithEligibility,
registerHatCreation, updateHatMetadata). Hats hierarchy now only matters for
vouching (via `combineWithHierarchy`); previously it also let any hat-admin
in the chain directly mutate eligibility, which was a vouching-bypass path —
a Delegate could either force-add a wearer or force-revoke an existing one
without governance.

Also retires the no-longer-reachable `NotAuthorizedAdmin` error and tightens
`hasAdminRights` to only return true for the superAdmin (was returning true
for Hats-hierarchical admins who, post-change, can't actually do anything).

Tests: 12 new regression tests in a dedicated SUPER-ADMIN GATING section
(every gated function gets a negative test against a hierarchical admin,
plus end-to-end "hierarchy admin cannot bypass vouching via direct Hats.mintHat"
and positive vouching tests with combineWithHierarchy on/off). Existing tests
that previously asserted hierarchical-admin writes succeeded were rewritten
to assert they now revert with NotSuperAdmin, with the superAdmin path
covering the success cases.

Upgrade: UpgradeEligibilitySuperAdminLockdown.s.sol provides the standard
3-step cross-chain rollout (Step1 Gnosis DD deploy → Step2 Arbitrum DD deploy
+ Hyperlane beacon dispatch → Step3 Gnosis verify) using v4 (probed FREE on
both chains, both surfaces, deterministic addr 0x881330E39EbD920e0406D066cf775168c3726239).
DryRun_GnosisUpgrade simulates against KUBI's live module on a Gnosis fork
under FOUNDRY_PROFILE=production and asserts: storage preservation, hierarchy
admin (caleb wearing EXECUTIVE_HAT) blocked from setWearer/setDefault/updateMeta
with NotSuperAdmin, KUBI_EXECUTOR round-trips (true,true)→(false,false)→cleared,
unaffiliated EOA blocked, hasAdminRights tightened correctly. All 9 checks pass.

No storage layout changes — safe upgrade for KUBI / Test6 / Poa / any other
org currently using the EligibilityModule beacon.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hudsonhrh hudsonhrh merged commit a365bb0 into main May 26, 2026
hudsonhrh added a commit that referenced this pull request Jun 1, 2026
… wins

Reconciles main's TaskManager v4/v5 + EligibilityModule lockdown (#159, #167,
#169) with the capability-hat refactor. Per direction, the capability-hat model
is canonical and main's new features were re-expressed in it.

Resolution highlights:
- TaskManager Layout: kept main's foldersRoot/organizerHatIds in their on-chain
  slots (live-org upgrade safety), appended cap-hat gate fields after, plus new
  budgetHat/editMetaHat/editFullHat. Append-only vs deployed v5 layout.
- v4 editable budgets: BOUNTY_CAP/PROJECT_CAP now gated by _requireBudgetEditor
  -> BUDGET capability hat (executor OR cap-hat), not the bitmask.
- v5 post-claim edits: updateTask (EDIT_FULL) + updateTaskMetadata (EDIT_META)
  gate via _hasCap, not _permMask.
- Folders kept verbatim (orthogonal; organizerHatIds stays a HatManager array gate).
- bootstrapGlobalPerms re-expressed as a deployer-time bulk global gate-hat setter
  (expands each mask into per-gate cap-hat assignments); deleted the dead bitmask
  helpers (_permMask, _syncPermissionHat, refcount machinery).
- setConfig(ROLE_PERM)/setProjectRolePerm extended to BUDGET/EDIT_META/EDIT_FULL,
  kept strict single-bit (InvalidCapMask).
- EligibilityModule: kept #167 superAdmin-only lockdown; the sole exception is
  setWearerEligibility via onlySuperAdminOrRevoker (RoleBundleHatter cascade).
- OrgDeployer: DeploymentParams keeps both capabilityHats/roleBundles and
  taskManagerPerms (complementary). Re-added _resolveRoleIndicesToHatIds.
- Added lens variant 12 ([budgetHat, editMetaHat, editFullHat]) for the new gates.

Security fix surfaced by the merge (pre-existing on the branch; DeployerTest was
previously excluded from verification): RoleBundleHatter.mintRole's pre-mint
eligibility reset unconditionally set wearers eligible, bypassing the vouching
gate (defaults.eligible=false) on self-service QuickJoin. Now gated on
hasSpecificWearerRules so it only re-enables previously-revoked wearers (preserves
re-mint-after-revoke) and never overrides a default-driven gate for fresh wearers.

Tests: 1423 passing / 0 failing / 16 skipped (obsolete bitmask tests). Main-era
bitmask tests adapted to capability-hat behavioral assertions. forge build/fmt clean.

Co-Authored-By: Claude Opus 4.8 (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