2.1.0
Horilla HR 2.1.0
This is a security release. We recommend everyone running 2.0.0 upgrade.
2.0.0 shipped on 30 August. Since then a full security and architecture review of the codebase
closed a number of access-control and injection issues, several of which are reachable without
authentication. If you run Horilla — self-hosted or otherwise — please upgrade.
⚠️ Breaking change: WhatsApp integration requires an App Secret
If you use the WhatsApp integration, message delivery stops until you set the Meta App
Secret in the WhatsApp credentials screen (find it under App Settings → Basic in the Meta
developer console).
This is deliberate. The webhook previously accepted any payload that reached it, and every POST
is acted on as the employee whose phone number the payload names — creating leave, attendance,
shift, asset and reimbursement requests, and able to mail that employee's documents to the
sender. Anyone who knew an employee's phone number could forge all of it.
Incoming webhooks are now verified against Meta's X-Hub-Signature-256 header. With no App
Secret configured the payload is refused and the reason logged, because accepting unverified
payloads would leave the endpoint exactly as exposed as before.
No other integration is affected.
Security fixes
Access control:
- Resumes were readable by anyone. The public job-application form accepted any resume id
and attached that file to the submitted application. Ids are sequential, so this exposed every
CV in the database, across all companies, with no login required. Resume access is now scoped
to the recruitment it belongs to. - API writes are permission-checked. Around fifteen endpoints accepted any authenticated
user's token for operations that should require a role: asset approvals and deletion,
attendance validation, overtime approval, tax brackets, reimbursement approval, bulk shift
deletion, and sending mail as the company. - Cross-employee data access closed. Bank details, contract and salary tabs, performance
objectives, announcements and notifications could be read or modified by employees other than
their owner. - Export column paths restricted. The quick-export feature accepted arbitrary attribute
paths, including ones that reached password hashes and bank account numbers. - Default Export Access is now an explicit setting. It previously defaulted to "everyone can
export" whenever no setting row existed, which meant a fresh install allowed every user to
export employee, payroll and attendance data with nothing recording that choice.
Injection and disclosure:
- Stored cross-site scripting in the helpdesk FAQ page, and user-authored rich text (ticket
descriptions and comments, recruitment descriptions, policy bodies, performance comments) is
now sanitised against an allow-list rather than rendered raw. - Server-side template injection in eleven mail-template render paths that could expose
password hashes and personal data. - A sandbox escape in the payroll tax-formula evaluator.
- The generated
SECRET_KEYwas downloadable by any logged-in user when running under
Docker, allowing session and token forgery. - Login lockout could be bypassed by rotating a request header.
- Onboarding portal tokens are no longer served through the API or embedded in log output.
Hardening:
- Two-factor codes are compared in constant time and expire after five failed attempts.
- API documentation is restricted to administrators outside debug mode.
- Idle sessions expire after 12 hours; secure cookie defaults now follow the production flag.
- The readiness endpoint no longer discloses database connection details.
- API rate limiting added; tokens are revoked when a password changes.
- 21 dependency CVEs cleared, the last one mitigated in application code.
Reliability
- Payslip generation is now atomic, so a failure part-way cannot leave a payslip half-written.
- Backup failures are no longer silent. A failed database dump previously uploaded whatever
stale file was on disk and reported success. - Background email is sent after the transaction commits, fixing mail that referenced records
that had not been saved yet. - Leave balance forecasting corrected; OKR progress calculation fixed.
- Scheduled jobs run in a single process rather than once per worker, so payslip runs, shift
rotation and backups no longer fire multiple times per interval. - Outbound integration calls now time out instead of hanging a worker indefinitely.
- Date columns indexed on the tables most often filtered by them.
Improvements
- Redesigned public career pages.
- New filter panel across list views; independent per-tab navigation in performance, helpdesk
and asset modules. - Kanban drag-and-drop consolidated onto the generic view layer.
- Audit logging now covers payroll, leave and attendance by default.
- JSON logging with request correlation ids; optional error reporting with personal data scrubbed.
- Continuous integration now runs against PostgreSQL with dependency, secret and code scanning.
Upgrading
docker pull horilla/horilla-hr:2.1.0Run migrations as usual. One data migration seeds the Default Export Access setting for existing
companies, preserving current behaviour.
If you use WhatsApp, set the App Secret before or immediately after upgrading — see the
breaking change above.