Audit fixes (2026-08) - #74
Open
JDeffner wants to merge 7 commits into
Open
Conversation
…dit C1, H2) Authentication state moves from setcookie()/$_COOKIE to the CodeIgniter session service, so a visitor can no longer grant themselves admin with document.cookie = "permissionLevel=2". Registration now builds the model array explicitly and forces permission = 1.
Task, board and person writes now build the model array from named request fields, so clients cannot set geloescht, erledigt, personenid or permission by adding an input. The admin person endpoint selects explicit columns and no longer returns the bcrypt hash (audit M6).
Board, column, task type and person names now go through esc(), with the 'js' context inside the inline onclick handlers, so a board named ');alert(1);// is no longer stored XSS.
… M4) The csrf filter is uncommented in the global before list, CSRF protection moves to the session store now that sessions exist, and main.js adds the X-CSRF-TOKEN header to every jQuery request. Token regeneration is off because the UI fires many POSTs per page load from one rendered token.
A benutzerAuthentification filter rejects permissionLevel < 1 on every creating, editing and deleting route of the tasks, spalten and boards groups, which matches what the README and the disabled styling promise. /tasks also redirects to /boards on a fresh database instead of crashing on a null board.
…DME status) Gruppennummer routes point at the real Admin\AdminController and return the number instead of var_dump, the debug welcome and test routes are gone, the CRUD modals name the type instead of '???', the scss script uses the sass dependency that is already installed, and the README says the project is unmaintained 2024 coursework.
One filter test posts a forged permissionLevel cookie at the admin filter and one feature test posts permission=2 at the registration route. app/Config/App.php gets a baseURL so the shipped HealthTest passes.
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.
Fixes the confirmed critical, high and medium findings of the 2026-08 audit, plus the cheap low ones. No framework or dependency upgrades, no changes outside the files the findings name.
Fixed
app/Filters/adminAuthentification.php:14)session()instead of$_COOKIEAdminAuthentificationFilterTestpostspermissionLevel=2as a cookie at the admin filter and gets the/deniedredirectpermission(BenutzerController.php:68)permission = 1; task, board and person writes do the sameBenutzerErstellenTestpostspermission=2at/benutzer/erstellenand the row lands withpermission = 1app/Views/pages/Tasks.php:28)esc()on every model value inTasks.php,TaskForm.php,SpalteForm.phpandNavbar.php, with thejscontext inside the inlineonclickhandlersgrep -rn "<?= \$" app/Viewsshows no unescaped model value left outside the framework error viewsapp/Config/Filters.php:41)csrfuncommented in the global before list, protection switched to the session store,main.jssendsX-CSRF-TOKENon every jQuery request from constants inHead.phpcsrf_token() => csrf_hash(); without the pair the filter rejects itBenutzerController.php:92)benutzerAuthentificationfilter rejectspermissionLevel < 1on the creating, editing and deleting routes of the tasks, spalten and boards groups; guests keep read accessphp -lon the config plus the filter registration; the read routes are untouchedAdmin/PersonenController.php:30)postPersonInfouses a new column-restrictedPersonenModel::getSecurePersonForEdit()id, vorname, nachname, email, permissiononly/taskscrashes on a fresh database (TasksController.php:18)/boards$firstBoard['id']onnullAdmin\AdminController::viewGruppennummer, and the handler returns'04'instead ofvar_dump(04)AdminControllerwelcomeandtestscssscript uses the already installedsass;node-sassdropped???app/Cells/CrudModals.phpdiffTwo changes outside the ledger were needed to make the suite run:
app/Config/App.phpgetsbaseURL = 'http://localhost:8080/'(the shippedHealthTestfails on an empty one), and CSRF token regeneration is off because the UI fires many POSTs per page load from one rendered token.Not fixed
TasksController.php:150). Left as is. It needs a validation plus transaction rewrite and the finding itself notes the unvalidated id buys nothing new now that writing needs a registered user.system/, 570 files). The ledger says this is CodeIgniter's documented manual-install layout and rewiring the project is not worth it.main.jsis a much larger diff than the security fixes here.README.md:165). Not mine to remove; that is Joel's call with his co-authors.$_POSTstill goes straight intoSpaltenControllerandAdmin\TaskartenController. TheirallowedFieldshold only presentation columns, so there is no privilege to gain, and the ledger did not list them.Checks
The
-d extension=sqlite3is needed on this machine only: the local PHP 8.2.12 build has the SQLite3 extension present but not loaded, and the shippedExampleDatabaseTestfails the same way without it onmain. Plainvendor/bin/phpunitpasses wherever SQLite3 is loaded.