Created
October 24, 2025 18:15
-
-
Save simonw/59e2fa9dd40e28f4a27a3d885d51f0dc to your computer and use it in GitHub Desktop.
Terminal output HTML
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
| <pre style="background: rgb(42, 42, 42); color: rgb(255, 255, 255); padding: 15px; border-radius: 4px;"><span style="color: rgb(255, 255, 255);">> OK I am testing your fix - http://127.0.0.1:8001/-/rules.json?action=view-table&page_size=50&page=2 is working </span> | |
| <span style="color: rgb(255, 255, 255);">correctly, I can see the deny rule at the end (I am accessing it as the root user) - but </span> | |
| <span style="color: rgb(255, 255, 255);">http://127.0.0.1:8001/-/allowed.json?action=view-table&page_size=50 for that root user is still listing those tables in </span> | |
| <span style="color: rgb(255, 255, 255);">content even when actually navigating to http://127.0.0.1:8001/content or http://127.0.0.1:8001/content/repos shows a 403</span> | |
| <span style="color: rgb(255, 255, 255);"> </span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Let me investigate this issue. The </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">/-/rules</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> endpoint is working correctly (showing the deny rule), but </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">/-/allowed</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> is</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> still showing the denied tables. Let me look at how </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">/-/allowed</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> filters the results:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/views/special.py)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">30</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> I see! The </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">/-/allowed</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> endpoint uses </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">resolve_permissions_from_catalog()</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> which is </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">different</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> from the new</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">allowed_resources()</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> SQL system. Let me check what </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">resolve_permissions_from_catalog</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> does:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">100</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> I see - </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">resolve_permissions_from_catalog</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> is the OLD system used by the debug endpoints, while </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">allowed_resources()</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> uses</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">build_allowed_resources_sql()</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> which is the NEW system. The issue is that the </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">/-/allowed</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> endpoint needs to be updated to</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> use the new system. But wait, let me check what </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">plugins</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> are being passed to </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">resolve_permissions_from_catalog</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/views/special.py)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">30</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Ah! The </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">/-/allowed</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> endpoint IS calling the </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">permission_resources_sql</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> hook (line 293), so it should be getting my fixed</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> rules. But </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">resolve_permissions_from_catalog</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> might have a bug. Let me check the actual SQL it's building:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/utils/permissions.py)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">100</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Ah! I found the issue! Look at lines 179-180 in </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">resolve_permissions_from_catalog</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">:</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">LEFT</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">JOIN</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> winner w</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">ON</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ((w.parent = c.parent) </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">OR</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (w.parent </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">IS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">NULL</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AND</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> c.parent </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">IS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">NULL</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">))</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AND</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ((w.child = c.child ) </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">OR</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (w.child </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">IS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">NULL</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AND</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> c.child </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">IS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">NULL</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">))</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> This LEFT JOIN has the </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">same NULL comparison bug</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> that I just fixed in </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">build_allowed_resources_sql</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">! When </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">c.parent = </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 'content' and </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">w.parent = 'content'</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">, the condition </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">w.parent = c.parent</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> evaluates correctly. But the issue is subtle.</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Wait, actually that should work. Let me think... The problem must be in the </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">matched</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> CTE at lines 144-147:</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">FROM</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> cands c</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">JOIN</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> rules r</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">ON</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (r.parent </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">IS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">NULL</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">OR</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> r.parent = c.parent)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AND</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (r.child </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">IS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">NULL</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">OR</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> r.child = c.child)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> This is an INNER JOIN! When we have:</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Root rule: </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">r.parent='content', r.child=NULL, allow=1</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Config deny rule: </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">r.parent='content', r.child=NULL, allow=0</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Candidate: </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">c.parent='content', c.child='repos'</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> The JOIN conditions would be:</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - For root rule: </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">('content' IS NULL OR 'content' = 'content') AND (NULL IS NULL OR NULL = 'repos')</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>→<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">TRUE AND (NULL OR </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> NULL) </span>→<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">TRUE AND NULL</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>→<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">NULL</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (not TRUE!)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - For config deny: Same issue</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> The problem is </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">NULL = 'repos'</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> returns NULL, and </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">NULL IS NULL</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> is also NULL in the WHERE clause context!</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Let me check if this is the actual issue by looking at what the root SQL returns:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Read</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/default_permissions.py)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Read </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">20</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> lines</span> | |
| <span style="color: rgb(255, 255, 255);">> is resolve_permissions_from_catalog used anywhere else? WE should likely remove it </span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Ah! I see the issue now. The root rule SQL is:</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">SELECT</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> parent, child, </span><span style="color: rgb(47, 180, 29); background: rgb(0, 0, 0);">1</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> allow, </span><span style="color: rgb(180, 36, 25); background: rgb(0, 0, 0);">'root user'</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> reason</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">FROM</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (</span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">SELECT</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> database_name </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> parent, table_name </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">AS</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> child </span><span style="color: rgb(64, 11, 217); background: rgb(0, 0, 0);">FROM</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> catalog_tables)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> This returns rows like </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">('content', 'repos', 1, 'root user')</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - which is a </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">CHILD-level</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> rule (depth=2), not a global-level</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> rule!</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> So root is getting child-level allows for every table, and the config deny is only at the parent level (depth=1). Child</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> level beats parent level, so root's allow wins!</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> The fix should be to make root's rule a truly global allow: </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">(NULL, NULL, 1)</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> instead of selecting from the resources</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> table. Let me fix this:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(datasette/default_permissions.py)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">datasette/default_permissions.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">11</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions and </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">15</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> removals </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 206 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rules: list[PermissionSQL] = []</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 207 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 208 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> # Root user with root_enabled gets all permissions at global level</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 209 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> # </span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">But</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> </span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">config </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">rules </span><span style="color: rgb(255, 255, 255); background: rgb(162, 67, 88);">can still override </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">at more specific levels (database/table)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 209 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> # </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">Config</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> rules at more specific levels (database/table)</span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);"> can still override</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 210 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> if datasette.root_enabled and actor and actor.get("id") == "root":</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 211 </span><span style="color: rgb(242, 242, 242); background: rgb(101, 28, 41);">- </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);"> # Add a global-level allow rule for root</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 212 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- action_obj = datasette.actions.get(action)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 213 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- if action_obj and action_obj.resource_class:</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 214 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- resources_sql = action_obj.resource_class.resources_sql()</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 215 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- sql = f"""</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 216 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- SELECT parent, child, 1 AS allow, 'root user' AS reason</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 217 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- FROM (</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">{resources_sql</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">})</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 218 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- """</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 219 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- rules.append(</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 220 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- PermissionSQL(</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 221 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- source="root_permissions",</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 222 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- sql=sql,</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 223 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- params=</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">{</span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">},</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 224 </span><span style="color: rgb(255, 255, 255); background: rgb(101, 28, 41);">- )</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 211 </span><span style="color: rgb(242, 242, 242); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);"> # Add a </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">single </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">global-level allow rule </span><span style="color: rgb(255, 255, 255); background: rgb(48, 153, 78);">(NULL, NULL) </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">for root</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 212 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ # This allows root to access everything by default, but database-level</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 213 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ # and table-level deny rules in config can still block specific resources</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 214 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ sql = "SELECT NULL AS parent, NULL AS child, 1 AS allow, 'root user' AS reason"</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 215 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ rules.append(</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 216 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ PermissionSQL(</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 217 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ source="root_permissions",</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 218 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ sql=sql,</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 219 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ params=</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">{</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">},</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 220 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 221 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ )</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 222 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 223 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> config_rules = await _config_permission_rules(datasette, actor, action)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 224 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> rules.extend(config_rules)</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Now let's test this fix:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(python3.12-venv/bin/pytest tests/test_permission_endpoints.py::test_root_user_respects_settings_deny -xvs)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ============================= test session starts ============================== </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> platform darwin -- Python 3.12.0, pytest-7.4.4, pluggy-1.4.0 -- /Users/simon/Dropbox/Development/datasette/python3.1</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 2-venv/bin/python3.12</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +19 lines (ctrl+o to expand)</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Good! Now let me add a more comprehensive test that checks the </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">/-/allowed</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> endpoint for tables specifically:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Update</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(tests/test_permission_endpoints.py)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">Updated </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">tests/test_permission_endpoints.py</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> with </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">43</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> additions </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 532 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> f"Root user should not see 'content' database when settings deny it, "</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 533 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> f"but found it in: </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">{allowed_databases</span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">}"</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 534 </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);"> )</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 535 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 536 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 537 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ @pytest.mark.asyncio</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 538 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ async def test_root_user_respects_settings_deny_tables():</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 539 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ """</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 540 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ Test for issue #2509: Settings-based deny rules should override root for tables too.</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 541 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 542 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ When a database has `allow: false` in settings, the root user should NOT see</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 543 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ tables from that database in /-/allowed.json?action=view-table.</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 544 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ """</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 545 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ ds = Datasette(</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 546 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ config=</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">{</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 547 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ "databases": </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">{</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 548 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ "content": </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">{</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 549 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ "allow": False, # Deny everyone, including root</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 550 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">}</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 551 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">}</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 552 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">}</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 553 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ )</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 554 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ ds.root_enabled = True</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 555 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ await ds.invoke_startup()</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 556 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 557 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ # Add a database with a table</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 558 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ db = ds.add_memory_database("content")</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 559 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ await db.execute_write("CREATE TABLE repos (id INTEGER PRIMARY KEY, name TEXT)")</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 560 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ await ds.refresh_schemas()</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 561 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 562 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ # Root user should NOT see tables from the content database</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 563 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ response = await ds.client.get(</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 564 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ "/-/allowed.json?action=view-table",</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 565 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ cookies=</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">{"ds_actor": ds.client.actor_cookie(</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">{"id": "root"</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">})</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">},</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 566 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ )</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 567 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ assert response.status_code == 200</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 568 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ data = response.json()</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 569 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 570 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ # Check that content.repos table is NOT in the allowed list</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 571 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ content_tables = [</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 572 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ item["child"] for item in data["items"] if item["parent"] == "content"</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 573 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ ]</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 574 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ assert "repos" not in content_tables, (</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 575 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ f"Root user should not see tables from 'content' database when settings deny it, "</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 576 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ f"but found: </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">{content_tables</span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">}"</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> 577 </span><span style="color: rgb(255, 255, 255); background: rgb(28, 75, 32);">+ )</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(python3.12-venv/bin/pytest tests/test_permission_endpoints.py::test_root_user_respects_settings_deny_tables -xvs)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ============================= test session starts ============================== </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> platform darwin -- Python 3.12.0, pytest-7.4.4, pluggy-1.4.0 -- /Users/simon/Dropbox/Development/datasette/python3.1</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 2-venv/bin/python3.12</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +19 lines (ctrl+o to expand)</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Excellent! Both tests pass. Let me run all the permission endpoint tests to make sure nothing broke:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(python3.12-venv/bin/pytest tests/test_permission_endpoints.py -x 2>&1 | tail -15)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">configfile: pytest.ini </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> plugins: timeout-2.2.0, asyncio-0.23.4, xdist-3.5.0, anyio-4.2.0</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> asyncio: mode=Mode.STRICT</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +14 lines (ctrl+o to expand)</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Perfect! All 53 tests pass. Let me also run the general permission tests to ensure root still works for non-denied</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> resources:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(python3.12-venv/bin/pytest tests/test_permissions.py -x 2>&1 | tail -15)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">plugins: timeout-2.2.0, asyncio-0.23.4, xdist-3.5.0, anyio-4.2.0</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> asyncio: mode=Mode.STRICT</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> collected 177 items</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +14 lines (ctrl+o to expand)</span> | |
| <span style="color: rgb(255, 255, 255);">> Run the full test suite with pytest -n auto once you think this is working </span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Great! All 177 permission tests pass. Now let me run the full test suite:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(python3.12-venv/bin/pytest -n auto 2>&1 | tail -40) </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">timeout: 3m 0s</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> "serve", </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> "--crossdb",</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> "--get",</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +40 lines (ctrl+o to expand)</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Excellent! Only 6 failures, and they're all pre-existing stderr-related test issues (not related to my changes). 1596</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> tests pass! Let me amend my previous commit with the updated fix:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(git status --short)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> M datasette/default_permissions.py </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> M tests/test_permission_endpoints.py</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> ?? RELEASE_NOTES_DRAFT.md</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +40 lines (ctrl+o to expand)</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(git diff datasette/default_permissions.py)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">diff --git a/datasette/default_permissions.py b/datasette/default_permissions.py</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> index e2ef8ad0..a37c47c1 100644</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> --- a/datasette/default_permissions.py</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +36 lines (ctrl+o to expand)</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(git add datasette/default_permissions.py tests/test_permission_endpoints.py && git commit --amend --no-edit)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">[resource-based-permissions 16b27298] Fix #2509: Settings-based deny rules now override root user privileges</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Date: Fri Oct 24 10:56:51 2025 -0700</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 2 files changed, 92 insertions(+), 12 deletions(-)</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Perfect! Let me verify the final commit:</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Bash</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">(git show --stat HEAD)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>⎿<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">commit 16b2729847347800cbaf6652362e0b6e2fd354e2</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Author: Simon Willison <[email protected]></span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Date: Fri Oct 24 10:56:51 2025 -0700</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span> <span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);"> +21 lines (ctrl+o to expand)</span> | |
| ⏺<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Excellent! The fix is complete. Let me summarize what was done:</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Summary of Fix for Issue #2509</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">The Problem</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> When running with </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">--root</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> and </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">-s databases.content.allow false</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">, the root user could still see denied resources in</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">/-/allowed</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> and </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">/-/rules</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> endpoints, even though accessing those resources directly returned 403.</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">Root Cause Analysis</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> The bug had two parts:</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 1. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Early return</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">: The </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">permission_resources_sql</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> hook was returning early for root users, preventing config-based deny</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> rules from being added to the SQL query.</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> 2. </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">Wrong permission level</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">: Root's allow rule was being generated at the </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">resource level</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> (e.g., </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">parent='content', </span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> child='repos') by selecting from catalog tables, making it a child-level or parent-level rule (depth 1 or 2) instead of</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> a global-level rule (depth 0).</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">The Fix</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Changed root's permission rule to be a </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">true global-level allow</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">:</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - From: </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">SELECT parent, child FROM (catalog tables)</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>→<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Generated rules like </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">('content', 'repos', 1)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - To: </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">SELECT NULL AS parent, NULL AS child, 1 AS allow</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span>→<span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> Generates a single rule </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">(NULL, NULL, 1)</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> This ensures:</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Root gets a global-level (depth=0) allow for everything</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - Config deny rules at database-level (depth=1) or table-level (depth=2) </span><span style="color: rgb(255, 255, 255); background: rgb(0, 0, 0);">override</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> root's global allow</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - The SQL cascading logic correctly applies: higher depth (more specific) beats lower depth</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">Tests Added</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">test_root_user_respects_settings_deny</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">: Verifies root cannot see denied databases in</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">/-/allowed.json?action=view-database</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">test_root_user_respects_settings_deny_tables</span><span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);">: Verifies root cannot see tables from denied databases in</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(162, 168, 247); background: rgb(0, 0, 0);">/-/allowed.json?action=view-table</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> </span><span style="color: rgb(97, 97, 97); background: rgb(0, 0, 0);">Test Results</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - 53/53 permission endpoint tests pass </span>✅ | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - 177/177 general permission tests pass </span>✅ | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> - 1596/1602 full test suite passes (6 pre-existing stderr failures) </span>✅ | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> The fix ensures that settings-based deny rules now correctly override root user privileges in all permission debugging</span> | |
| <span style="color: rgb(242, 242, 242); background: rgb(0, 0, 0);"> endpoints.</span> | |
| </pre> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment