Files
2026-04-30 21:31:32 +02:00

275 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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*