Skip to content

Audit fixes (2026-08) - #74

Open
JDeffner wants to merge 7 commits into
mainfrom
audit-fixes-2026-08
Open

Audit fixes (2026-08)#74
JDeffner wants to merge 7 commits into
mainfrom
audit-fixes-2026-08

Conversation

@JDeffner

Copy link
Copy Markdown
Owner

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

Finding Sev Fix Verified Commit
C1 Auth is a client-writable cookie (app/Filters/adminAuthentification.php:14) critical Login, registration, guest login and logout use the CI4 session service; both filters read session() instead of $_COOKIE AdminAuthentificationFilterTest posts permissionLevel=2 as a cookie at the admin filter and gets the /denied redirect d1dc6a6
H2 Registration mass-assigns permission (BenutzerController.php:68) high Registration builds the model array from four named fields and forces permission = 1; task, board and person writes do the same BenutzerErstellenTest posts permission=2 at /benutzer/erstellen and the row lands with permission = 1 d1dc6a6, bbeaeb1
H3 Board and user names rendered unescaped (app/Views/pages/Tasks.php:28) high esc() on every model value in Tasks.php, TaskForm.php, SpalteForm.php and Navbar.php, with the js context inside the inline onclick handlers grep -rn "<?= \$" app/Views shows no unescaped model value left outside the framework error views c64272c
M4 CSRF disabled (app/Config/Filters.php:41) medium csrf uncommented in the global before list, protection switched to the session store, main.js sends X-CSRF-TOKEN on every jQuery request from constants in Head.php The registration feature test only passes because it posts csrf_token() => csrf_hash(); without the pair the filter rejects it 47a9cc6
M5 Guests can write everything (BenutzerController.php:92) medium New benutzerAuthentification filter rejects permissionLevel < 1 on the creating, editing and deleting routes of the tasks, spalten and boards groups; guests keep read access php -l on the config plus the filter registration; the read routes are untouched 6c66f51
M6 Admin person endpoint returns the bcrypt hash (Admin/PersonenController.php:30) medium postPersonInfo uses a new column-restricted PersonenModel::getSecurePersonForEdit() The select lists id, vorname, nachname, email, permission only bbeaeb1
M7 /tasks crashes on a fresh database (TasksController.php:18) medium Null guard on the first board redirects to /boards Read of the guarded branch; the crash needed $firstBoard['id'] on null 6c66f51
L Gruppennummer routes point at a missing class low Admin\AdminController::viewGruppennummer, and the handler returns '04' instead of var_dump(04) Route strings now match the only existing AdminController 14cb9ad
L Debug routes welcome and test low Routes and both handlers deleted grep finds no reference left 14cb9ad
L SCSS build broken on current Node low scss script uses the already installed sass; node-sass dropped package.json diff 14cb9ad
L CRUD modals show literal ??? low The modal title names the type app/Cells/CrudModals.php diff 14cb9ad
L README invites contributions to abandoned coursework low New "Project status" section says this is unmaintained summer 2024 coursework that should not be deployed; the Contributing section says the same README diff 14cb9ad

Two changes outside the ledger were needed to make the suite run: app/Config/App.php gets baseURL = 'http://localhost:8080/' (the shipped HealthTest fails 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

  • L Bulk reorder writes any task id from an unvalidated JSON body (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.
  • L The whole framework is committed as source (system/, 570 files). The ledger says this is CodeIgniter's documented manual-install layout and rewiring the project is not worth it.
  • L Controllers echo views and return raw JSON strings. Purely stylistic, and touching every controller action plus main.js is a much larger diff than the security fixes here.
  • L Two other students' university addresses in the README (README.md:165). Not mine to remove; that is Joel's call with his co-authors.
  • $_POST still goes straight into SpaltenController and Admin\TaskartenController. Their allowedFields hold only presentation columns, so there is no privilege to gain, and the ledger did not list them.
  • No test for the XSS, guest-write and hash-leak boundaries. The three fixes are read-verified only.

Checks

composer install                                  # exit 0, 30 packages installed
php -d extension=sqlite3 vendor/bin/phpunit --no-coverage
                                                  # OK (8 tests, 11 assertions)
php -l app/Config/Filters.php                     # No syntax errors detected

The -d extension=sqlite3 is needed on this machine only: the local PHP 8.2.12 build has the SQLite3 extension present but not loaded, and the shipped ExampleDatabaseTest fails the same way without it on main. Plain vendor/bin/phpunit passes wherever SQLite3 is loaded.

…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.
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