This commit is contained in:
2026-04-30 21:31:32 +02:00
parent e22bbde336
commit e6a09f6c95
16 changed files with 1929 additions and 1 deletions

274
.paul/codebase/concerns.md Normal file
View File

@@ -0,0 +1,274 @@
# Concerns & Technical Debt
**Analysis Date:** 2026-04-30
---
## Security Issues
### [HIGH] Database credentials committed to version control
- **File:** `application/config/database.php`
- **Detail:** Production MySQL username (`host420804_db`) and plaintext password (`VanMzwjUn85ySRyR`) are hardcoded and committed to git.
- **Impact:** Anyone with access to the repository has full database access. This is especially dangerous given the repo is on a shared hosting environment.
- **Fix:** Move credentials to a `.env` file or server-level environment variables. Add `application/config/database.php` to `.gitignore` and use a `database.php.example` as template.
### [HIGH] FTP credentials committed to version control
- **File:** `.vscode/ftp-kr.json`
- **Detail:** Production FTP credentials are committed — host `host420804.hostido.net.pl`, username `www@centrumcopy.com.pl`, and plaintext password (`JHycfrHnyEAYsJHtR26C`). Protocol is plain FTP (not SFTP).
- **Impact:** Full read/write access to the production server's `public_html` for anyone with repo access.
- **Fix:** Add `.vscode/ftp-kr.json` to `.gitignore`. Switch from FTP to SFTP. Store credentials outside the repository.
### [HIGH] Unprotected install controller accessible in production
- **File:** `application/controllers/install.php`
- **Detail:** The `install` route is publicly routable (`$config['install/*(.*)'] = 'install/$1'` in `application/config/routes.php`). The controller can create users and insert database records. No authentication check. Visiting `/install/init` in a browser would recreate seed data, including writing admin users.
- **Impact:** Any visitor can trigger `/install/user/admin` which resets admin credentials to `admin/admin`.
- **Fix:** Delete or disable the install controller in production. At minimum wrap every method with an IP whitelist or a production check using `IN_PRODUCTION`.
### [HIGH] Force-login backdoor controller
- **File:** `application/controllers/admin/force.php`
- **Detail:** `Force_Controller::login()` logs in as user ID 1 with no password check whatsoever, then sets the admin session. It is reachable at `/admin/force/login` via the catch-all route `$config['admin/(.*)'] = 'admin/$1'`.
- **Impact:** Any visitor can visit `/admin/force/login` and immediately gain full admin access without credentials.
- **Fix:** Delete this file immediately. It appears to be a developer debug shortcut left in the codebase.
### [HIGH] Debug output (`print_r($_POST)`) in the login controller
- **File:** `application/controllers/admin/user.php`, line 29
- **Detail:** `print_r($_POST)` is called on every POST to the login form. This outputs all submitted POST data (including username and password) directly to the browser response.
- **Impact:** Credentials are exposed in the HTTP response body. May also interfere with redirects.
- **Fix:** Remove this line immediately.
### [HIGH] Unescaped HTML output of page content in front-end views
- **Files:** `application/views/front/page_show.php` (line 5), `application/views/front/page_contact.php` (line 5), `application/views/default_layout.php` (lines 73, 75)
- **Detail:** `$page->content`, `$page->header`, `$szybki_kontakt->content`, and `$szybki_kontakt->header` are echoed raw with no escaping. Admin-saved HTML content is intentional (TinyMCE WYSIWYG), but if the admin account is compromised, an attacker could inject arbitrary JavaScript.
- **Impact:** Stored XSS is possible through the admin panel if admin credentials are stolen.
- **Fix:** Accept risk intentionally for trusted admin HTML content, but enforce strong auth (see CSRF and password hashing concerns). Document this explicitly.
### [HIGH] No CSRF protection on any form
- **Files:** All forms — `admin_login.php`, `admin/page_edit.php`, `admin/password.php`
- **Detail:** Zero CSRF tokens are generated or validated on any POST form. Kohana 2.x includes no built-in CSRF protection. The forms rely solely on JavaScript-side validation, which is easily bypassed.
- **Impact:** Any site can submit a form on behalf of a logged-in admin, changing page content or admin credentials.
- **Fix:** Generate a per-session CSRF token in the base controllers and validate it on every POST.
### [HIGH] Weak password hashing (SHA-1 with weak salt)
- **Files:** `application/controllers/admin/user.php` (lines 33, 93, 97), `application/controllers/install.php` (lines 29, 36, 87, 93)
- **Detail:** Passwords are hashed using `sha1($salt . $password)`. The salt is generated as `md5(rand(100000,999999) . $username . $email)` — a low-entropy 6-digit random number seeded with predictable data. SHA-1 is cryptographically broken for password storage.
- **Impact:** If the database is breached, passwords are easily crackable via rainbow tables or brute force.
- **Fix:** Migrate to `password_hash()` / `password_verify()` with `PASSWORD_BCRYPT` or `PASSWORD_ARGON2ID`.
### [MEDIUM] No rate limiting or lockout on the login endpoint
- **File:** `application/controllers/admin/user.php`
- **Detail:** The login form has no brute-force protection. There is no account lockout, CAPTCHA, or IP-based rate limiting after repeated failures. The `last_failed` timestamp is recorded but never acted upon.
- **Impact:** Automated brute-force attacks against the admin login are unimpeded.
- **Fix:** Add lockout logic after N failures within a time window, or integrate a CAPTCHA (Kohana has a Captcha library available).
### [MEDIUM] Session ID never regenerated after login
- **File:** `application/config/session.php` (line 43: `'regenerate' => 0`), `application/controllers/admin/user.php`
- **Detail:** `session.regenerate` is set to `0` (disabled). No manual session ID regeneration occurs on successful login. This allows session fixation attacks.
- **Fix:** Call `session_regenerate_id(true)` immediately after successful login, or set `'regenerate' => 1`.
### [MEDIUM] Cookies are not HttpOnly
- **File:** `application/config/cookie.php` (line 28: `'httponly' => FALSE`)
- **Detail:** Session cookies are accessible via JavaScript. Combined with any XSS vector, this enables session hijacking.
- **Fix:** Set `'httponly' => TRUE`.
### [MEDIUM] Cookies are not Secure (no HTTPS enforcement)
- **File:** `application/config/cookie.php` (line 23: `'secure' => FALSE`), `.htaccess`
- **Detail:** No HTTPS redirect is configured in `.htaccess`. Cookies are sent over plain HTTP. Google Maps API is loaded over HTTP (`http://maps.google.com/...`).
- **Fix:** Add HTTPS redirect to `.htaccess`. Set cookie `'secure' => TRUE`. Use `https://` for external resources.
### [MEDIUM] Google Maps v2 API key committed to repository
- **File:** `application/config/gmaps.php`
- **Detail:** A Google Maps API v2 key is hardcoded and committed. Google Maps v2 has been deprecated and shut down for years.
- **Fix:** Remove the key. Migrate to Google Maps v3 (which uses domain restriction, not a key in the same way), or use an alternative mapping service.
### [LOW] `IN_PRODUCTION` flag is `false` in the committed index.php
- **File:** `index.php` (line 16: `define('IN_PRODUCTION', false)`)
- **Detail:** The production flag is hardcoded as `false`. This means Google Analytics is never included on the live site (guarded by `IN_PRODUCTION`). It also suggests dev and production use the same code file with no environment separation.
- **Fix:** Use an environment variable or server-detected value to set this flag per environment.
---
## Technical Debt
### [HIGH] `filter_var()` emulation with security bypass
- **File:** `index.php` (lines 6880)
- **Detail:** If the PHP `filter` extension is not installed, `filter_var()` is replaced with a stub that always returns `true`, meaning all IP/URL validation silently passes without actually validating anything.
- **Impact:** If running on a server without the filter extension, all input validation using `filter_var` is bypassed.
- **Fix:** Require the `filter` extension explicitly in server requirements. Remove the stub.
### [HIGH] Deprecated `mysql_*` functions used in system database driver
- **File:** `system/libraries/drivers/Database/Mysql.php`
- **Detail:** The `Mysql` driver uses `mysql_connect()`, `mysql_query()`, `mysql_close()`, etc. These functions were removed in PHP 7.0. The application is configured to use the `mysqli` driver (`application/config/database.php`), but the old `Mysql.php` driver still exists and would fail completely if the config were changed.
- **Impact:** The old driver is dead code but signals the PHP version constraint this codebase was designed for.
- **Fix:** Confirm `mysqli` driver is always used. Delete `system/libraries/drivers/Database/Mysql.php` or mark it clearly as unsupported.
### [HIGH] Commented-out dead code throughout controllers
- **Files:** `application/controllers/admin/user.php` (lines 810, 2930, 46, 63), `application/controllers/base_admin.php` (lines 13, 31, 4041), `application/controllers/base_front.php` (lines 1217, 4043)
- **Detail:** Large blocks of commented-out functionality exist: disabled profiler, disabled cookie-based redirect, disabled homepage layout, disabled `__destruct` rendering. These represent abandoned design decisions.
- **Fix:** Remove commented-out code. Use git history if rollback is ever needed.
### [MEDIUM] `stara/` directory — entire old website committed to repository
- **File:** `stara/` directory
- **Detail:** An old version of the website (`index.htm`, Flash files, old HTM pages, images) is committed to the repository root and likely deployed to production alongside the current application.
- **Impact:** Old pages may be publicly accessible. Increases repository size. Potential security exposure if old files have vulnerabilities.
- **Fix:** Remove `stara/` from the repository. If archival is needed, move it to a separate repository or offline storage.
### [MEDIUM] Google Maps v2 integration (defunct API)
- **Files:** `application/config/gmaps.php`, `application/controllers/front/page.php`, `modules/gmaps/`
- **Detail:** The entire `gmaps` module integrates Google Maps JavaScript API v2, which was shut down in 2013. The map on the contact page will not render.
- **Fix:** Rewrite the contact page map using Google Maps v3 or an alternative (e.g., Leaflet with OpenStreetMap). Remove the `gmaps` module.
### [MEDIUM] Adobe Flash integration
- **Files:** `flash/centrumcopy.swf`, `flash/expressInstall.swf`, `application/views/default_layout.php` (lines 4446, 94102)
- **Detail:** Adobe Flash Player support was dropped by all browsers at end of 2020. The Flash banner on the homepage cannot be displayed.
- **Fix:** Remove the Flash files and `swfobject` integration from the layout. Replace with a static image or CSS/HTML5 animation.
### [MEDIUM] Menu and navigation structure hardcoded in two separate places
- **Files:** `application/config/application.php` (`$config['menu_nav']`), `application/views/admin_layout.php` (admin sidebar links)
- **Detail:** The navigation menu is defined once in config (rendered for front-end) and a parallel, nearly duplicate list exists hardcoded in the admin layout view. Adding a new page requires editing both files.
- **Fix:** Drive the admin sidebar from the same data source as the front-end menu config.
### [LOW] TinyMCE 3.x (ancient WYSIWYG editor)
- **File:** `js/tiny_mce/`
- **Detail:** TinyMCE 3.x is bundled. TinyMCE is now at version 7. Version 3 is no longer maintained and may have known XSS vulnerabilities in the editor itself.
- **Fix:** Upgrade to TinyMCE 6/7 or replace with a modern editor (e.g., Quill, ProseMirror).
### [LOW] jQuery and jQuery UI versions are unversioned/unknown
- **Files:** `js/jquery.min.js`, `js/jquery-ui.min.js`
- **Detail:** No version number is visible in the filenames. Given the use of IE7 polyfills and Flash, these are likely very old versions.
- **Fix:** Replace with a pinned, modern version (jQuery 3.x). Consider migrating away from jQuery UI entirely.
### [LOW] IE7 polyfill scripts loaded from external CDN via HTTP
- **Files:** `application/views/default_layout.php` (line 15), `application/views/admin_layout.php` (line 10), `application/views/admin_login.php` (line 10)
- **Detail:** `ie7-js.googlecode.com` is loaded as an external HTTP script. Google Code shut down in 2016; this URL returns a 404 or is hijacked. IE7 support is irrelevant in 2024+.
- **Fix:** Remove all IE7 conditional comments and scripts.
---
## Performance Concerns
### [MEDIUM] N+1 query risk on every page load — `szybki-kontakt` page
- **File:** `application/controllers/base_front.php` (line 31)
- **Detail:** Every page load on the front-end executes `ORM::factory('page')->where('name', 'szybki-kontakt')->find()` in the base controller constructor. This is a database query run on every single front-end request, even pages that don't display the sidebar contact snippet.
- **Fix:** Cache this result in a static variable or application-level cache. Consider storing it in config if the content rarely changes.
### [MEDIUM] Database query benchmarking enabled in production config
- **File:** `application/config/database.php` (line 28: `'benchmark' => TRUE`)
- **Detail:** Query benchmarking adds overhead to every database call.
- **Fix:** Set `'benchmark' => FALSE` in production, or use the `IN_PRODUCTION` flag to switch configs.
### [LOW] No HTTP caching headers configured
- **Files:** `.htaccess`, `application/config/`
- **Detail:** No `Cache-Control`, `Expires`, or ETags are configured for static assets (CSS, JS, images). The `.htaccess` has no asset caching rules.
- **Fix:** Add browser caching rules in `.htaccess` for static file extensions.
### [LOW] All front-end JavaScript loaded synchronously in `<head>`
- **File:** `application/views/default_layout.php` (lines 1830)
- **Detail:** 7+ JS files are loaded with blocking `<script>` tags in the document `<head>`, including the defunct Flash embed script.
- **Fix:** Move scripts to bottom of `<body>` or use `defer`/`async` attributes.
---
## Dependency Risks
### [HIGH] Kohana 2.3.x — end-of-life framework
- **Files:** `system/` directory
- **Detail:** The application uses Kohana 2.3 (evidenced by the `$Id: index.php 4134 2009-03-28...` comment and Kohana framework version markers). Kohana has been end-of-life since approximately 2016. No security patches, no community support.
- **Impact:** Any framework-level vulnerability will never be patched. The framework depends on deprecated PHP 5.2+ APIs.
- **Fix:** Plan a migration to a maintained framework (Laravel, Symfony, or even Kohana 3.x). This is a major long-term effort.
### [HIGH] PHP 5.2 minimum — framework requirement
- **File:** `index.php` (line 45: `version_compare(PHP_VERSION, '5.2', '<')`)
- **Detail:** The codebase targets PHP 5.2. PHP 5.x has been end-of-life since December 2018. Modern PHP (8.x) has breaking changes for deprecated functions used in this codebase (especially `mysql_*` functions removed in PHP 7).
- **Impact:** If the server is upgraded to PHP 8.x, the application will break. If the server remains on PHP 5.x, it runs on an unsupported, vulnerability-riddled runtime.
- **Fix:** Verify the current PHP version in production. Audit for PHP 7/8 incompatibilities and fix them before any server upgrade.
### [HIGH] SwiftMailer v3 (abandoned)
- **File:** `system/vendor/swift/`
- **Detail:** SwiftMailer v3 is bundled in the framework vendor directory. SwiftMailer was abandoned and superseded by Symfony Mailer. Version 3 is extremely old.
- **Fix:** If email sending is ever needed, replace with PHPMailer or Symfony Mailer.
### [MEDIUM] Google Maps API v2 (shut down 2013)
- See Technical Debt section above. The map simply does not work.
### [MEDIUM] Adobe Flash (deprecated end of 2020)
- See Technical Debt section above.
---
## Maintainability
### [HIGH] No database schema documentation
- **Files:** `.paul/codebase/` (referenced in `CLAUDE.md` but `db_schema.md` does not exist), no migration files anywhere
- **Detail:** There is no schema file, no migration system, and no documentation of the database structure. The only way to know the schema is to connect to the production database.
- **Fix:** Reverse-engineer the current schema and create `.paul/codebase/db_schema.md`. Consider introducing a migration tool (e.g., Phinx) for future schema changes.
### [HIGH] No `.gitignore` for sensitive or generated files
- **Files:** Project root
- **Detail:** No `.gitignore` exists (or it is not present in the working tree). As a result, `application/config/database.php` (with production credentials), `.vscode/ftp-kr.json` (with FTP credentials), `application/cache/` (generated cache files), and `application/logs/` (log files) are all committed.
- **Fix:** Create a `.gitignore` immediately. At minimum exclude: `application/config/database.php`, `.vscode/ftp-kr.json`, `application/cache/*`, `application/logs/*`, `uploads/`.
### [MEDIUM] Error from log reveals a known PHP compatibility bug
- **File:** `application/logs/2024-05-18.log.php`
- **Detail:** Log entry: `Creating default object from empty value in application/controllers/admin/user.php at line 10`. This is caused by the commented-out `$this->message->password_success = ...` code — `$this->message` is never initialized as an object, so assigning to it on a modern PHP version throws an error. The code in `User_Controller::password()` calls `$this->message->password_success` (line 108) which would also fail.
- **Fix:** Initialize `$this->message` as `new stdClass()` in the `Base_Admin_Controller` constructor, or replace with an array.
### [MEDIUM] Duplicated `forward()` method in both base controllers
- **Files:** `application/controllers/base_admin.php` (lines 6886), `application/controllers/base_front.php` (lines 6785)
- **Detail:** The `forward()` method is identical in both base controllers. Any change must be made in two places.
- **Fix:** Extract to a shared parent controller or a trait (PHP 5.4+).
### [MEDIUM] Duplicated `error404()` method in both base controllers
- **Files:** `application/controllers/base_admin.php` (lines 5161), `application/controllers/base_front.php` (lines 5160)
- **Detail:** Same issue as `forward()` — identical implementation in two classes.
- **Fix:** Same resolution — extract to a shared parent.
### [LOW] Copyright date is frozen at 2010
- **Files:** `application/views/default_layout.php` (line 84), `application/views/admin_layout.php` (line 104), `application/views/admin_login.php` (line 51)
- **Detail:** All footer copyright notices show `© 2010`. The site appears abandoned from a maintenance perspective.
- **Fix:** Use `date('Y')` dynamically or update to current year range.
### [LOW] Inactive/unused models committed
- **Files:** `application/models/gallery.php`, `application/models/gallery_image.php`, `application/models/news.php`
- **Detail:** Models for `gallery`, `gallery_image`, and `news` exist but no corresponding controllers, routes, or views for these features are present (the gallery and news routes exist in `routes.php` but route to non-existent controllers `front/gallery` and `front/news`).
- **Impact:** Visiting `/galeria` or `/aktualnosci` will produce a 404 or Kohana error.
- **Fix:** Either implement these features or remove the dead models and routes.
---
## Missing Practices
### [HIGH] No test suite whatsoever
- **Detail:** There are no test files anywhere. Kohana's `unit_test` module is commented out in `application/config/config.php`. There is no PHPUnit setup, no test directory, no test runner.
- **Impact:** Any change to the codebase carries unknown risk. Regressions cannot be detected automatically.
- **Fix:** At minimum, add PHPUnit and write smoke tests for the login flow and page rendering. The Kohana unit_test module can serve as a starting point.
### [HIGH] No error tracking / monitoring
- **Detail:** The only error tracking is file-based logging to `application/logs/`. There is no integration with Sentry, Bugsnag, Rollbar, or equivalent. Log files are PHP files (which is a Kohana convention to prevent direct web access, but is not a proper log management solution).
- **Fix:** Integrate Sentry (free tier) for real-time error tracking.
### [HIGH] No deployment procedure documented
- **File:** `CLAUDE.md`
- **Detail:** The `CLAUDE.md` explicitly notes `(Uzupełnij procedurę deploy)`. The only deployment mechanism is an FTP auto-upload via VS Code extension. There is no deployment script, no CI/CD pipeline, no staging environment concept.
- **Fix:** Document the deploy procedure. Consider a simple deployment script. Move away from FTP (use SFTP or SSH-based deployment).
### [MEDIUM] No environment separation (dev vs production)
- **Detail:** There is a single set of config files. `IN_PRODUCTION = false` is hardcoded. Database credentials are for production. Debug toolbar (`application/config/debug_toolbar.php`, `auto_render => TRUE`) is enabled and would render if the debug module were active.
- **Fix:** Implement environment detection (e.g., via `$_SERVER['HTTP_HOST']` or a server env var) and load environment-specific config overrides.
### [MEDIUM] No HTTPS enforcement
- **Files:** `.htaccess`, `application/config/cookie.php`
- **Detail:** No redirect from HTTP to HTTPS exists in `.htaccess`. The site is likely accessible over plain HTTP, exposing session cookies and login credentials over the wire.
- **Fix:** Add HTTPS redirect to `.htaccess`. Enable `'secure' => TRUE` in cookie config after HTTPS is enforced.
### [LOW] No Content Security Policy (CSP) headers
- **Detail:** No CSP headers are set anywhere. Given the TinyMCE editor and Google Maps integration, a CSP would need to be permissive, but even a basic policy would reduce XSS risk.
- **Fix:** Add `Content-Security-Policy` header in `.htaccess` or in the base controller output.
### [LOW] Google Analytics configuration is empty
- **File:** `application/config/application.php` (line 11: `$config['google_analytics'] = ''`)
- **Detail:** The Google Analytics tracking code is empty. The legacy GA v1 code in `default_layout.php` (lines 109118) uses the deprecated `ga.js` library, not `gtag.js`. Even if a tracking ID were provided, it would use an obsolete tracking method.
- **Fix:** Either remove Google Analytics entirely or migrate to GA4 with `gtag.js`.
---
*Concerns audit: 2026-04-30*