Skip to content

Commit 96e3bf0

Browse files
committed
docs: actualizar CHANGELOG, README y CLAUDE.md para v1.14.1
1 parent 37fece1 commit 96e3bf0

8 files changed

Lines changed: 120 additions & 149 deletions

File tree

‎.env.example‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,4 @@ LOGIN_MAX_ATTEMPTS=5
3434
LOGIN_LOCKOUT_MINUTES=15
3535

3636
# Versión de la aplicación
37-
APP_VERSION=1.14.0
37+
APP_VERSION=1.14.1

‎CHANGELOG.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@ Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
> Note: Entries before `1.3.1` may reference legacy paths (`config/`, `controllers/`, `model/`) that were moved to `app/Config/`, `app/Controller/`, and `app/Model/`.
88
9+
## [1.14.1] — 2026-07-15
10+
11+
### Fixed
12+
13+
- **Weak password bypass via password reset** — `AuthController::resetPassword()` only checked that `new_password` matched `confirm_password`, never enforcing the 8-character minimum already required by `ProfileController::changePassword()` and `UserController::validateUser()`. A valid reset token could be used to set an empty or trivially short password. Now enforces `strlen($newPassword) >= 8` before calling `Auth::consumeResetToken()`.
14+
15+
### Changed
16+
17+
- `CLAUDE.md` — removed two Notes bullets duplicating facts already covered in the Key Files table and Security Patterns section (`session_start_secure()` usage, `AuthMiddleware::session()` wiring); documented the resetPassword fix above
18+
- `README.md` — restructured for a public-facing audience:
19+
- Added Table of Contents and a Screenshots section (title + description + image per feature, no tables)
20+
- `Features` reorganized into three grouped categories instead of a 25-bullet list that duplicated the `Security` section's implementation detail; cross-linked to `Security` for specifics
21+
- `Installation` no longer pastes the full `.env` contents — points to `.env.example` instead
22+
- `Project Structure` reduced from a fully-annotated recursive tree to a 2-level overview, with a pointer to `CLAUDE.md` for the file-by-file breakdown
23+
- Fixed a stale/vague comment in the `Testing` setup instructions referencing a non-existent "`.env.testing` section in docs"
24+
925
## [1.14.0] — 2026-07-07
1026

1127
### Added

‎CLAUDE.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ SMTP_PORT=587
4747
4848
APP_URL=http://localhost/Encriptacion_PHP/public
4949
APP_TIMEZONE=America/Bogota
50-
APP_VERSION=1.14.0
50+
APP_VERSION=1.14.1
5151
5252
CACHE_ENABLED=true
5353
CACHE_TTL_USERS=60
@@ -246,6 +246,7 @@ Loaded **only** on pages that set `$useDataTables = true`. The flag loads the fu
246246
- **CSRF**: `App\Core\Csrf::token()` generates a `bin2hex(random_bytes(32))` token stored in `$_SESSION['csrf_token']`; all POST forms include `<input type="hidden" name="_csrf">` with this value; controllers call `$this->verifyCsrf($redirectPath)` which uses `hash_equals()` to compare — prevents timing attacks; token is **rotated** after each successful verification (`unset($_SESSION['csrf_token'])` in `Csrf::verify()`)
247247
- **Logout is POST-only** — `/logout` route only accepts POST; `header.php` renders a `<form>` with CSRF token; `AuthController::logout()` calls `verifyCsrf()` before processing — prevents logout CSRF via `<img>` or link
248248
- Reset tokens: `bin2hex(random_bytes(32))` raw token sent in email URL; SHA-256 hash stored in `password_resets.token` — 1-hour expiry, single-use (`used = 1` after consumption)
249+
- **Password reset minimum length**: `AuthController::resetPassword()` enforces the same 8-character minimum as `ProfileController::changePassword()` and `UserController::validateUser()` before calling `consumeResetToken()` — previously only checked `new_password === confirm_password`, allowing arbitrarily short/empty passwords via the reset flow (fixed 2026-07-15)
249250
- **User enumeration prevention**: `AuthController::forgotPassword()` always returns the same generic message regardless of whether the email is registered — email is sent silently if the token was created
250251
- All DB queries in `app/Model/User.php` use MySQLi prepared statements
251252
- Email sanitized with `filter_var($email, FILTER_SANITIZE_EMAIL)` before DB queries
@@ -318,5 +319,3 @@ composer test:integration # Auth class only
318319
- Error/success messages use unified session flash: `$_SESSION['message']` and `$_SESSION['icon']`. Rendered via `views/layouts/messages.php`. Never pass them via URL query params
319320
- Auth views use `<button type="submit">` (not `<input type="submit">`); POST detection uses `isset($_POST['btnXXX'])` — not `!empty()` — since `<button>` without a `value` attribute submits an empty string
320321
- User delete flow uses `.js-delete-user` buttons with `data-delete-url`, `data-name`, `data-username`; confirmation handled in `public/js/users-delete.js`
321-
- `session_start_secure()` is called in `app/Config/autoload.php` — always use this helper instead of bare `session_start()` to ensure `httponly`/`samesite`/`secure` options are applied; `Auth::restoreFromCookie()` runs immediately after on every request
322-
- `AuthMiddleware::session()` is currently only wired into `SessionController::guard()` (after `timeout()`, before `auth()`) — other protected controllers do not yet check session revocation; if that changes, keep `timeout() → session() → auth()` order since `session()` needs `$_SESSION['session_token']` to still be present

0 commit comments

Comments
 (0)