105 Commits

Author SHA1 Message Date
Álvaro Tejero Cantero
4cb0e452ff
Merge branch 'develop' into iop-vc-preview
Bring iop-vc-preview up to develop b438676d6c.

Conflicts:
- migrations.clj: develop's 0157-migrate-legacy-file-data and
  0158-del-forbidden-file-library-rels keep develop's order, and the
  branch's 0800-0808 follow them. No file names collide, so nothing
  is renumbered.
- files_update.clj: develop's save-path rewrite wins (commit-by-commit
  rejection, page-scoped validation, the file carrying ::changes and
  ::rejected). The branch's op-log save is ported onto it instead of
  the earlier [file op-changes media-index] triple:
  process-changes-and-validate attaches the media fix-up remap as
  ::media-index, and update-file* builds the op-log vector from the
  accepted changes (remapped) behind the repair changes. A branch
  save persists only when develop's saved? holds, and its audit and
  log outcome is "rejected" when the first commit is refused.
  The tpoint threading, persist-branch-file!, the public
  invalidate-caches!, and the branch audit props are kept.
- Repair and validation timing tests: take develop's calls, since
  process-changes-and-validate returns a file again.
- binfile_test.clj: keep the branch import-overwrite test and
  develop's library relation tests.
- dashboard.cljs, dashboard_test.cljs: union of requires and tests.
- viewer.cljs: develop's optional page-id plus the branch's pr-id.
- left_header.cljs: develop's raw-svg logo, recent-files href and
  project link, inside the branch's wrapper with the review badge,
  branch title and branch bar. The unused deprecated-icon require
  and nav-to-project go.
- en.po, es.po: three-way merge by msgid. Every entry of both sides
  is kept, develop's removals of a duplicate errors.save-retrying and
  of integrations.mcp-server.title.beta stand, and labels.retry
  carries develop's references plus the branch's.

Adaptations to develop:
- email.clj, files_pull_request.clj: the review request email joins
  develop's template registry and goes through send-template, as the
  comment emails it mirrors now do. The registry coverage test lists
  it, and the rollback test of the pull request commands injects its
  failure into send-template, the function the create path now calls.
- rpc_file_pull_request_test.clj: pr-flags is read at test time
  instead of at namespace load. The loaded value held the production
  defaults, which now enable the admin console (#12475), and it
  replaced th/state-init's test flags, so creating a profile called
  nitrate and failed with :nitrate-not-configured.

AI-assisted-by: claude-opus-5-5
Signed-off-by: Álvaro Tejero Cantero <alvorithm@teje.ro>
2026-11-11 17:51:20 +01:00
alonso.torres
cb26e8a39b ✨ Keep autosave and the workspace alive when saves fail
- A save that failed on a short platform failure (a lost or refused
  database connection, lack of resources, a server shutting down, a
  serialization failure or deadlock, a socket or HTTP timeout, a host
  that did not resolve, an interruption) answered 500, which stops
  autosave. It now answers 503, which the frontend retries. Other I/O
  and database errors keep answering 500, timeouts 504.
- A save answered not-found no longer replaces the workspace with the
  access page. It is a terminal save error: the workspace stays open
  with the failed save notice, report and reload link. When edit rights
  are lost, the notice says how many changes could not be saved.
- A library sync marked the library synced three seconds later,
  whatever happened to its save. A rejected sync was undone but stayed
  marked: copies stayed out of date and the "updates available" notice
  never came back. The mark now waits for the save, a rollback of the
  sync brings the notice back, and a failed mark (e.g. the user just
  became a viewer) is logged instead of leaving the workspace.
- While saves fail the workspace is read-only. The view-mode banner
  says editing is disabled because changes can't be saved, with the
  Inspect banner's size and bold lead and no Done button. Users can
  still switch pages, pan, zoom and inspect. The lock lifts when a save
  lands and brings back the mode the user had or picked meanwhile; the
  WebGL context lock keeps the workspace read-only if it lasts longer.
  The lock lives in app.main.data.persistence.lock.
- A viewer is never locked. A role change that leaves the user a viewer
  keeps the edits that lost permission, queued and on screen, and shows
  the count notice again. The role change that brings edit rights back
  releases them and rolls them back on screen, as a rejected save would.
  Any other terminal failure is released by the next role change or file
  opened.

The debug faults `io` (socket timeout, 503) and `eof` (500) cover both
sides of the I/O rule.
2026-11-05 23:59:32 +01:00
Álvaro Tejero Cantero
1342b27f6f
Merge remote-tracking branch 'origin/develop' into iop-vc-preview
Merges develop at f6f9df9a1a. Twelve files conflicted; each keeps
both intents.

files_update.clj: develop's per-stage validation timings wrap the
branch save path. `update-file` keeps the branch's early timer and
passes it to `update-file*`, inside develop's try/catch timing log.
`process-changes-and-validate` times the changes application and
still returns the file, the fixed-up change vector and the media
index. The audit props carry develop's revn and modified-at plus the
branch props. The timing test destructures the new return shape.

v3.clj keeps the branch-file overwrite guard and takes develop's
`configure-import-transaction`. migrations.clj puts develop's 0155
to 0157 before the branch's 0800 to 0805, so the branch's stay the
tail of the vector.

errors.cljs checks the merged and archived branch refusals before
develop's retrying case. left_header.cljs keeps the branch wrapper,
the review badge and the branch bar on develop's markup: the status
dot without icons and the DS library icon on the shared badge.
palette.scss and assets.scss keep the branch clearance terms on
develop's logical properties. The test runners, requires and
deftests are unions.

AI-assisted-by: claude-opus-5-5
Signed-off-by: Álvaro Tejero Cantero <alvorithm@teje.ro>
2026-10-28 06:28:08 +01:00
Andrey Antukh
70b317a976
🐛 Harden upload path handling and validation error bodies (#12067)
* 🚑 Reject forged upload paths outside multipart

Transit bodies can smuggle upload maps whose path points at any
server file; the schema only checks the shape and handlers open
the path directly.

app.rpc now injects a server-side multipart flag that bodies cannot
forge, and methods declare their upload keys with the new
multipart-only-params metadata, enforced centrally after schema
validation with a params-validation error. Covers all nine upload
methods, including chunked and management flows.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Sanitize raw explain in generic validation error bodies

The generic :validation branch returned ex-data untouched, so errors
carrying the raw malli explain (live schema objects, e.g. storage
invalid-storage-metadata) broke JSON encoding mid-stream and clients
got truncated bodies. Apply the same clean treatment the known codes
already use: drop the raw explain and attach its humanized text.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Add e2e path traversal tests for binfile and media upload

Add two e2e suites that hand-write transit bodies with a forged
["~#path", ...] upload path (no transit library needed) and expect
400 validation/params-validation. They cover import-binfile :file,
upload-file-media-object :content and upload-chunk :content, plus a
plain-JSON string-path control case. Before the multipart-path fix
they failed (500 io-exception, invalid-image, 200 stored chunk);
after it they pass.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Use multipart helper in binfile SVG test setup

The SVG import setup uploaded through plain command!, so the new
multipart-only guard rejected it with params-validation. Switch
the setup to multipart-command! like the other upload tests.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Use multipart helper in remaining upload tests

The multipart-only upload guard rejects plain RPC calls carrying
upload maps. Five tests with real files still used command! and
failed with params-validation. Switch them to multipart-command!
while keeping the forged-content tests on plain command!.

AI-assisted-by: muse-spark-1.3-contributor
2026-10-17 16:01:04 +02:00
Álvaro Tejero Cantero
863c7cab1b
🐛 Fix the defects the review of the branch commands found
The source notes on files_branch.clj listed 32 review findings. This
commit fixes the 21 that are defects of the code; each fix carries a
regression test that failed before it and passes after.

Concurrency: merge, update from main, rename, archive and delete now
re-read the branch row under the advisory lock and work off that row,
so a concurrent merge or update can no longer leave a ten-year base
pin behind or release a superseded base twice. Delete and archive take
the branch file lock, create and reopen of a pull request refuse a
branch that a concurrent merge closed, and create checks the branch
quota under the lock. Merge, update and delete publish their msgbus
messages only after the transaction commits.

Merge, update and materialize re-validate after the repair: merge and
update refuse a result that stays invalid, materialize logs it. The
change log row carries the repair changes, and the three paths drop
the cached file summary under :redis-cache.

Contracts: a :conflict refusal answers 409 instead of 500, resolution
values must be :main or :branch, and update from main accepts the
expected-main-revn token the frontend now sends from a loaded diff.

Smaller fixes: create refuses a blank name, writes no file_data row,
and a rename reaches the branch file's own name. Deleting a kept
merged branch keeps its merged status. get-branch-diff reports the
creation time of the current base snapshot. Rename, archive and delete
report their duration and outcome. An image used only by a library
colour is copied into the target file. Dead fallbacks and dead
deleted-at checks are gone.

AI-assisted-by: claude-opus-5-5
2026-10-06 10:36:55 +02:00
Andrey Antukh
acd146f6f4
🐛 Restore rate-limit headers and add Retry-After on 429 (#11895)
* 🐛 Restore rate-limit headers and add Retry-After

The account-lockout change replaced the header-forwarding 429 handler
with a body-only one, so existing RPC rate-limit responses lost their
x-rate-limit-remaining and x-rate-limit-reset headers. Account lockout
never sent Retry-After either.

Make handle-error :rate-limit preserve ::http/headers and add a
retry-after header when the exception carries a non-nil :ttl in
seconds, keeping the current JSON body. Add focused tests for both the
lockout and the RPC limiter paths.

Document activation, defaults, password/LDAP scope, Redis fail-open
behavior, and the lockout risk, and record the final HTTP contract in
the backend auth memory.

Refs #11397

AI-assisted-by: deepseek-v4.1-flash

* 🐛 Add Retry-After to RPC 429 and expose headers in CORS

Address review follow-ups on the account-lockout 429 contract:

- The RPC limiter now sets retry-after in its 429 headers (seconds
  until the longest rejecting limit resets), so it matches the
  account-lockout response and the HTTP standard.
- CORS exposes retry-after, x-rate-limit-remaining, and
  x-rate-limit-reset so browser clients can read them.
- Use backticks for Retry-After and account-locked in the docs for
  consistency with nearby sections.

Refs #11397

AI-assisted-by: deepseek-v4.1-flash
2026-09-24 12:19:29 +02:00
Andrey Antukh
1c7a73ec16
✨ Add account lockout after failed login attempts (#11402)
* ✨ Add account lockout after failed login attempts

Implement per-account brute-force protection using a Redis-backed
failed-login counter. After 5 failed attempts within 15 minutes, the
account is temporarily locked out and all login attempts (including
with the correct password) are rejected with a 429 response.

Closes #11397

AI-assisted-by: longcat-2.0

* 🐛 Bind LDAP session to directory-verified profile

The account-lockout change added a shortcut that preferred the
profile matching the typed email over the one returned by the LDAP
directory. These can differ with aliases, UPNs, or multi-valued mail
attributes, letting a user with valid LDAP credentials bind a session
to another Penpot account.

Keep the typed-email profile only for lockout checks. After LDAP
succeeds, resolve the session profile from the directory identity as
before and clear failed attempts on that profile.

AI-assisted-by: deepseek-v4.1-flash
2026-09-23 12:31:50 +02:00
María Valderrama
66876f16b2
✨ Error page for instances without admin-console configuration (#11649)
* ✨ Error page for instances without admin-console configuration

* 📎 Code review
2026-09-15 07:24:55 +02:00
Andrey Antukh
37dab75e1a Merge remote-tracking branch 'origin/staging' into develop 2026-09-10 20:29:44 +02:00
Andrey Antukh
4ce459d720
🐛 Escape LDAP filter values and use directory email in retrieve-user (#11085)
Fix LDAP injection vulnerability (T5-N1-03) where the client-supplied email was used directly in the LDAP search filter without escaping RFC 4515 special characters (*, (, ), \, NUL), and the profile email was taken from client input instead of the LDAP directory attribute.

Changes:
- Add escape-ldap-filter-value per RFC 4515 section 3
- Apply escaping in search-user before building LDAP filter
- Add get-attr helper for multi-valued LDAP attributes
- Fix retrieve-user to use directory email (attrs-email) instead of client email
- Use cuerdas.core instead of clojure.string

Closes #11084

AI-assisted-by: mimo-v2.5-pro
2026-09-10 16:39:35 +02:00
Andrey Antukh
81c3b3cd56
📎 Update copyright name on file header (#11346) 2026-08-25 11:55:10 +02:00
Andrey Antukh
dd4a163217
🐛 Remove internal error details from HTTP error responses (#11288)
* 🚑 Remove internal error details from HTTP error responses

PostgreSQL exceptions, I/O exceptions, and unhandled errors were
leaking raw database messages (table names, constraint names,
SQLSTATE codes), filesystem paths, and internal exception details
to API clients via :hint, :state, and :path response fields.

Remove these fields from server-error responses while keeping
full error context in server-side logs for operators.

Closes #11287

AI-assisted-by: mimo-v2.5-pro

* 🚑 Strip internal fields and map PG errors to safe messages

Complete the security fix for GHSA-r8wx-23q6-w3gf by addressing
the incomplete redaction found in code review.

Add strip-internal-fields helper to dissoc :hint, :state, :path,
and :context from error response data in three handlers that
previously passed raw ex-data through to clients:

- handle-error :internal
- handle-exception :default (else branch)
- handle-error :assertion (else branch)

Add pgsql-state->message to map PostgreSQL SQLSTATE codes to safe,
client-facing messages (e.g. 23505 → "A conflicting entry already
exists") instead of returning raw PG error text. Include :message
in all PSQLException response branches.

Add regression tests asserting :hint, :state, :path, :context are
absent from responses for :internal and unhandled ex-info errors.

Closes #11287

AI-assisted-by: mimo-v2.5-pro

* 🚑 Keep :hint in error protocol, fix unsafe sources

Refine the security fix based on code review feedback.

Keep :hint as part of the error protocol — it is essential for
controlled error communication. Remove it from strip-internal-fields
(which now only strips :state, :path, :context).

Fix the actual sources of unsafe :hint values:

- http/middleware.clj: replace (ex-message cause) with safe static
  strings for IllegalArgumentException, RequestTooBigException, and
  EOFException. These :validation errors return ex-data verbatim
  to clients, so raw exception messages were leaking internals.

- PSQLException handler: use :hint instead of :message for the
  SQLSTATE-mapped messages, staying consistent with the error
  protocol.

Update tests to assert :hint is present (with safe static values)
in :internal and unhandled ex-info responses, and absent only from
bare RuntimeException and IOException responses.

Closes #11287

AI-assisted-by: mimo-v2.5-pro
2026-08-21 12:43:23 +02:00
Andrey Antukh
45405a018b 📎 Add minor changes on error reports 2026-07-23 13:06:46 +02:00
Andrey Antukh
4a8fb5af53 Merge remote-tracking branch 'origin/staging' into develop 2026-06-01 13:15:57 +02:00
Yamila Moreno
ddba2ffa75
📎 Update Kaleidos Copyright (#9929) 2026-05-29 11:24:58 +02:00
Juanfran
e6848170c8 🎉 Show dedicated screen when Nitrate is unavailable 2026-05-21 14:47:32 +02:00
Andrey Antukh
0dfa62a5b6
🐛 Improve error reporting on request parsing failures (#8805)
Include request URI and status in frontend handle-response error data,
and add request path/context to backend IOException handler logs and
response body. Previously these errors had no identifying information
about which endpoint or request caused the failure.
2026-03-26 15:42:49 +01:00
Andrey Antukh
d80ba1856a
✨ Add several improvements to frontend error reporting
* ✨ Add major improvement on error handling

* ✨ Add the ability to store frontend reports

* 📎 Add PR feedback changes
2026-02-04 12:45:38 +01:00
Andrey Antukh
363b4e3778
♻️ Make the SSO code more modular (#7575)
* 📎 Disable by default social auth on devenv

* 🎉 Add the ability to import profile picture from SSO provider

* 📎 Add srepl helper for insert custom sso config

* 🎉 Add custom SSO auth flow
2025-11-12 12:49:10 +01:00
Andrey Antukh
28cf67e7ff
🎉 Add management RPC API (#7700)
* 🎉 Add management RPC API

And refactor internal http auth flow

* 📎 Adjust final url namings

* 📚 Update changelog
2025-11-10 17:10:59 +01:00
Andrey Antukh
49721c0bcd ✨ Add better logging context report on worker runner 2025-11-04 12:44:38 +01:00
Andrey Antukh
25521b18ff ✨ Make the restriction errors report as warning to logging 2025-10-14 19:04:02 +02:00
Andrey Antukh
e6e71e9278 ✨ Add minor enhacement for error reporting 2025-04-01 20:24:07 +02:00
Andrey Antukh
f8ffae75c4 ⚡ Make feature resolved on team load
That simplifies features retrieval to simple get
2025-04-01 20:01:21 +02:00
Andrey Antukh
895450c9d5 ✨ Report restriction errors to logger 2025-02-11 13:52:12 +01:00
alonso.torres
ecb7f0a2f6 ✨ File history versions management 2024-10-29 14:23:35 +01:00
Andrey Antukh
51ecbf15a9 ⬆️ Update yetti and http server dependency 2024-10-22 20:23:38 +02:00
Andrey Antukh
782d733bc9 ✨ Improve error response formatting 2024-10-18 17:19:29 +02:00
Andrey Antukh
af389fe63a 🐛 Fix error reporting regression 2024-07-25 17:17:49 +02:00
Andrey Antukh
79fbbe0bee 📎 Don't report invalid image validation errors 2024-04-10 15:31:49 +02:00
Andrey Antukh
d435b17452 🐛 Fix io exception incorrect reporting 2024-04-08 11:18:26 +02:00
Andrey Antukh
07b8a2a6e6 ✨ Restrict http methods on RPC handlers 2024-03-05 19:47:29 +01:00
Andrey Antukh
15f81e557c 🐛 Fix unexpected exception on importing some binary files 2023-12-15 17:54:26 +01:00
Andrey Antukh
03518a8da1 ✨ Add the ability to stream events on rpc methods 2023-12-13 14:20:07 +01:00
Andrey Antukh
2295d085d3 ⚡ Improve performance on error formating and reporting 2023-11-27 14:25:12 +01:00
Andrey Antukh
bb5a4c0fa5 ✨ Update yetti and adapt for ring-2.0 2023-11-27 14:25:12 +01:00
Andrey Antukh
0081db4770 ✨ Improve error reporting 2023-11-20 19:42:46 +01:00
Andrey Antukh
3c64955b93 ✨ Add efficiency improvements to backend error reporting 2023-11-13 18:33:28 +01:00
Andrey Antukh
57c83b5d53 ♻️ Refactor internal backend error handling 2023-11-07 12:48:31 +01:00
Andrey Antukh
d80aa7593b 🐛 Fix unexpected exception on encoding error response 2023-08-24 11:37:59 +02:00
Andrey Antukh
81facd58c9 ✨ Improve error report of invalid image 2023-08-08 12:57:49 +02:00
Andrey Antukh
046f501152 ✨ Improve error reporting context 2023-08-02 14:51:12 +02:00
Andrey Antukh
5ca3d01ea1 🎉 Add malli based validation and coersion subsystem 2023-05-17 16:05:29 +02:00
Andrey Antukh
b462ac019a 🐛 Fix typo on error type 2023-03-16 22:33:35 +01:00
Andrey Antukh
aafbf6bc15 ♻️ Refactor cocurrency model on backend
Mainly the followin changes:

- Pass majority of code to the old and plain synchronous style
  and start using virtual threads for the RPC (and partially some
  HTTP server middlewares).
- Make some improvements on how CLIMIT is handled, simplifying code
- Improve considerably performance reducing the reflection and
  unnecesary funcion calls on the whole stack-trace of an RPC call.
- Improve efficiency reducing considerably the total threads number.
2023-03-14 12:30:27 +01:00
Andrey Antukh
bb055a3c84 ♻️ Refactor logging subsystem and error reporting 2023-02-02 13:38:04 +01:00
Andrey Antukh
db689d151e ♻️ Refactor profile and session handling
- makes the profile access more efficient (replace in-app joins to a
  simple select query on profile table
- add partial support for access-tokens (still missing some RPC methods)
- move router definitions to specific modules and simplify the main http
  module definitions to simple includes
- simplifiy authentication code related to access-tokens and sessions
- normalize db parameters with proper namespaced props
- more work on convert all modules initialization to use proper specs
  with fully-qualified keyword config props
2023-01-18 10:51:58 +01:00
Andrey Antukh
8bad9d8340 ♻️ Refactor loggers/audit, auth/oidc, and http/clent modules 2022-12-05 08:53:00 +01:00
Andrey Antukh
7f7efc5760 ✨ Improve exception formating on backend 2022-12-05 08:51:54 +01:00
Andrey Antukh
37ad04d2a6 🎉 Add robust concurrency limiter for RPC 2022-11-07 10:05:56 +01:00