Repository navigation
fix(roles): save attribute_permissions as [] so read-only roles can see their rows - #1875
Draft
dawsontoth wants to merge 1 commit into
Draft
dawsontoth wants to merge 1 commit into
dawsontoth wants to merge 1 commit into
Conversation
…ee their rows The role editor wrote `attribute_permissions: null` for every table when Pick Attributes was off (since #894). Harper's role validation accepts and stores `null`, but `verifyPerms` reads `.length` from it whenever the role searches with `get_attributes: ['*']`, so every row read of a table the role can read answers 500 "There was an error when trying to choose an operation path" (TypeError: Cannot read properties of null). Browse's list, filtered list and row editor all send `['*']`, so the user saw the table listed and never its rows. Reproduced on Harper 4.7.40 and 5.3.1; the same role with `[]` reads normally and is still denied every other table. The template now writes `[]`, and preparePermissionForSave sends any `null` it finds as `[]`, so re-saving a role stored with the broken shape repairs it. Closes #1299 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Code Review
This pull request ensures that attribute_permissions is saved as an empty array [] instead of null to prevent 500 errors in HarperDB. The changes update defaultCalculator.ts and preparePermissionForSave.ts to convert null values to [], with careful handling of potential prototype pollution (e.g., __proto__ keys). Comprehensive tests have been added to verify these fixes across different scenarios. I have no additional feedback to provide as there are no review comments.
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||||||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
⊙ Problem
A user whose role grants
readon a table could see the table in Browse but none of its rows (#1299). Since #894 (Nov 2025), the role editor has saved every table with"attribute_permissions": nullwhenever Pick Attributes is off, which is the default. Harper's role validation accepts and storesnull. ButverifyPermsreads.lengthfrom it whenever that role searches withget_attributes: ['*'], so the read fails with HTTP 500There was an error when trying to choose an operation path(TypeError: Cannot read properties of null (reading 'length')). The grid's list query, the filtered list and the row editor all send['*']. I reproduced this on Harper 4.7.40 and 5.3.1. The same role with[]reads normally and is still denied every other table.💡 Solution
"attribute_permissions": []for an unpicked table instead ofnull.preparePermissionForSavesends anynullit finds as[]. That covers a role opened from storage, JSON pasted in, or a hand edit, so re-saving a broken role repairs it.[]andnullmean the same thing to Harper: no attribute scoping (getTableAttrPermsonly scopes on a non-empty array). The only difference is that[]doesn't crash the'*'expansion.⚖️ Alternatives
'*'when the signed-in role's table entry isnull. Explicit lists don't crash. This would fix already-stored roles with no admin action, but it changesget_attributesin three read paths (list, filtered list,search_by_id) plus export, and relationship and computed attributes behave differently under an explicit list. Not done;harper#3143is the cleaner fix for stored roles.ATTR_PERMS_ARRAY_MISSING), so[]is the only safe value.🔧 Changes
defaultCalculator.ts:buildCurrentwrites[]for the unpicked case.preparePermissionForSave.ts: the doc comment states the rule and links harper#3143. The non-elevated path (including a database-scopedstructure_userarray) now returns a copy with every table'snullreplaced by[]. The copy is rebuilt withObject.fromEntries, so a database or table named__proto__stays an own key. Only an exactnullis rewritten: scopedattribute_permissionsand legacyattribute_restrictionspass through untouched, and the input is not mutated. The elevated-role path is unchanged, since it drops table permissions anyway.usePermissions.ts: one comment that saiddefaultCalculatorwritesnullnow says earlier builds saved it. Readers still acceptnull, because stored roles carry it.✅ Verification
add_role, thenalter_rolewith the editor document,read: trueon one table) plus a user in it, and replayed Browse's exact request bodies as that user. Withnull:describe_allanddescribe_tablereturned 200, whilesearch_by_value,search_by_conditionsandsearch_by_idwith['*']all returned 500. I then built thealter_rolepayload with the fixedcalculateDefaultPermissions+preparePermissionForSavefrom the server's realdescribe_all, starting from the storednullrole, and applied it. After that the list andsearch_by_idreturned the rows on both versions, the other table still returned 403, andinsertreturned 403.defaultCalculator.test.tschecks that the template writes[]both for a fresh role and for one stored withnull.preparePermissionForSave.test.tscoversnullbecoming[]without mutating the input, scoped and legacy entries left untouched, the database-scopedstructure_userpath and__proto__names. Its shared fixture moves to[]so the "untouched" cases still mean untouched.EditRoleModal.test.tsxdrives the real modal: saving a new read-only role, and re-saving one stored withnull, both send[]throughalter_role.origin/stage, 6 tests fail, and the modal test's diff is exactly"attribute_permissions": null. Reverting each file alone turns its own tests red (1 and 4). SwappingObject.fromEntriesfor plain assignment fails the__proto__test.vitest runpassed (395 files, 3670 tests).tsc -b,oxlintanddprint checkall exited 0. The pre-commit hook passed without--no-verify.EditRoleModal.tsx.Closes #1299
🤖 Generated with Claude Code
🤖 Generated by Anthropic Claude (Opus 5.5); posted via @dawsontoth.
Related PRs: none found
Complexity: easy
Review-Coverage: authored=claude; ran=codex,gemini; blocked=cursor-composer(no-receipt),domain(auth),cursor-muse(no-receipt); declined=cursor-grok,cursor-kimi; rounds=2; full=1 @ d878005
Review-Attention: read ~3m (raised: degraded review) @ d878005