Download Latest Version opensparrow-3.5.zip (3.7 MB) Google Add to Preferred Sources
Home / 4.1
Name Modified Size InfoDownloads / Week
Parent folder
opensparrow-4.1.zip 2026-10-10 3.8 MB
opensparrow-4.1.zip.sha256 2026-10-10 86 Bytes
README.md 2026-10-10 6.9 kB
Totals: 3 Items   3.8 MB 0

Added

  • Two-factor login (email code) — an optional second factor, toggled in System → Settings → Login Security and off by default. After a correct password, accounts with a contact email set receive a 6-digit code (valid 60 seconds, 5 attempts, sending throttled to 3/min per IP and per username) and must enter it to finish logging in; accounts without an email keep password-only login. Only the HMAC hash of the code is stored in the login session, wrong codes feed the existing spw_login_attempts lockout, and a delivery failure fails closed with a generic message instead of silently skipping the factor. Delivery reuses the existing email settings (Cron → Email Delivery / SMTP); new i18n strings are added in all 20 languages.
  • Streaming answers in Ask AI — POST api/rag.php?action=query with stream:true now replies as NDJSON: a meta event with the sources first, token-by-token delta events while the model works, and a final done event carrying the post-processed answer, extracted follow-up suggestions and cleaned-up record links. The panel renders the preview live and replaces it with the answer of record on done; the Stop button aborts the stream and releases the concurrency slot. Non-streaming callers keep the old single JSON response, and demo mode still answers 200 with plain JSON.
  • Record links in Ask AI answers — [View: table:id] markers in an answer now render as clickable links to the exact record. The grid context prepends the hidden id column (it is excluded from the displayed columns by design), so the model is able to cite real identifiers instead of guessing from labels; markers for unknown tables are dropped at render time.
  • View support in the External API — an API key from System → API can now bind a registered PostgreSQL view (ordinary or materialized) in addition to a table: columns and filter values are re-introspected against the live database on every save and every request, filter type coercion uses the shared ApiFilterValueCoercer for both tables and views, and views sort by their first selected column (they have no guaranteed id). A source that no longer fits the live schema answers 404 fail-closed — never a 500 from stale configuration. The save response now returns a flat list, so server-assigned ids and generated keys are adopted correctly instead of regenerating on every re-save.

Changed

  • Admin CSRF is header-only — public/admin/api.php reads the token exclusively from X-CSRF-Token; the body/form fallback is gone, and raw $_GET reads in admin endpoints were replaced with os_request().
  • CSV import refactored into App\Service — CsvReader, CsvFileValidator, RowCaster, ImportRepository, CsvImportService and CopyImportService moved out of the endpoint, which is now a pure router.
  • Menu rendering extracted — the 400-line access-filtering/catalog logic moved from templates/menu.php to includes/menu_builder.php; templates and public entry pages read the session through os_session_get() instead of $_SESSION.
  • Cron output is plain text — ETL, notifications and anonymization scripts emit no HTML, share the new etl_finish_job_log() / etl_persist_watermark_if_changed() helpers, and print generic messages while driver details go to the server error log only; the admin Cron tab renders the output with textContent.
  • Version info moved to the footer — the login page lost its version block; the footer now shows the version and a linked logo.

Removed

  • The mocks/ design mockup — mocks/share-tables-mock.html (450 lines of unused HTML) is deleted.

Fixed

  • Enum values are whitelisted server-side on every write path — grid PATCH/INSERT, mass edit and the record form (UpdateMapper) reject values outside the column's option list with a specific message; a hand-crafted POST can no longer bypass the <select> into the database. Pinned by EnumWhitelistGuardTest across all write paths.
  • Driver-error text no longer reaches the client — generic messages replace pg_last_error()/getMessage() in CSV import, demo seed, setup API, automation actions and the FK options loader; automation failures log details server-side instead of persisting them into spw_automation_runs.error_msg (which the admin API serves back). Pinned by AutomationErrorLeakTest and AdminPublicErrorLeakTest.
  • CSV COPY direct-stream column-order corruption — values taken in CSV-header order are now written to columns derived from the same ordering; previously a reordered mapping silently inserted rows into shifted columns with a success response.
  • JS sinks escape API data — health.js, setup.js, user-menu.js, files-page.js and views.js escape interpolated values (escHtml()/textContent), closing the XSS path behind the backend leak above.
  • The Ask AI on/off switch works — chat_enabled was saved by the admin for years but read by nothing; it now gates the query API with 403 (before rate-limit and semaphore), hides the menu entry and the FAB, and defaults to enabled when unset.
  • Newly created records open on the first tab — the stale #tab fragment was dropped from the create redirect.

Upgrade notes

  • No database migration is required and no configuration keys are removed by this release. The 2FA toggle lives in the existing settings config key (two_factor_enabled) and the code reuses the spw_users.email column added by the 3.3_user_contact migration — apply that migration first if it is still pending, otherwise the Login Security tab note applies and 2FA simply cannot engage.
  • Two-factor login is opt-in: enable it in System → Settings → Login Security; make sure the email delivery settings (System → Cron → Email Delivery) are working first, otherwise logins for accounts with an email will fail closed at the code step.
  • Custom admin integrations must send the CSRF token in the X-CSRF-Token header — a token posted in the request body is no longer accepted by public/admin/api.php. The bundled admin panel already does this (apiFetch).
  • External API definitions that point at a view must have that view registered and visible in Admin → Views; a stored filter that no longer matches the live column type fails closed with 404 instead of being skipped.
  • External API responses for view-backed keys are ordered by the view's first selected column ascending; table-backed keys keep id DESC.
  • The mocks/ directory is removed; nothing in the application references it.

Full changelog: https://github.com/wrobeltomasz/OpenSparrow/compare/4.0...4.1

Download OpenSparrow

Source: README.md, updated 2026-10-10