Commit Graph
647 Commits
Author SHA1 Message Date
39303f19cd fix(editor): stabilize image generation, layer editing and controls (#6992)
* fix(editor): stabilize image workflows and editing controls

* docs(editor): add desktop and mobile visual validation evidence

* fix(editor): synchronize quick edit model events and register imagePreferences in test coordinator

* fix(settings): enforce authoritative global image-gen policy and non-admin preference persistence

- Enforce authoritative administrator global image_gen_enabled disable in get_user_setting, do_generate_image, and image_editing_provider_available, preventing non-admin user preference overrides while preserving user-disabled opt-outs and can_generate_images privilege checks across direct, gallery, and editor paths.
- Filter non-admin preference persistence in saveImagePreferences to user preference keys only (image_model, image_edit_model, image_quality), preventing global policy toggles from leaking into user preferences.
- Return accurate 200 OK Response from saveImagePreferences after fallback persistence succeeds so settings UI displays 'Saved' instead of false 403 error, while retaining visibility of real errors.
- Load per-user preferences via readImagePreferences in initImageSettings so non-admin selections persist and display correctly after page reload.
- Clear per-user model preferences on successful admin save (POST /api/auth/settings) via PUT /api/prefs/{key} with value: null to prevent stale per-user preferences from shadowing updated global settings.
- Support clearing keys in PUT /api/prefs/{key} when value is null.
- Add comprehensive Python regression tests for global-disabled/user-enabled, global-enabled/user-disabled, and authorized generation, plus Node and browser regression tests for non-admin persistence, toggle isolation, reload, and admin clearing.

* fix(settings): invalidate shared appConfig cache near auth settings write

* fix(test): isolate test_tool_policy unit suite from external model endpoint state

* fix(test): isolate web_fetch failure regression from contextual follow-up heuristics

* fix(test): isolate test_tool_policy from cross-test approval gate state

Ensure _patch_loop_basics disables TOOL_APPROVAL_GATE_ENABLED so routing tests in test_tool_policy run in the intended opt-out posture even when prior tests populate DATA_DIR with skills or untrusted context. Remove unnecessary forced_tools and contextual web mock workarounds.

* fix(settings): isolate non-admin image toggle and surface preference cleanup errors

- Restrict image generation availability toggle to admin users in imageModels.js, disabling the switch for non-admins and reverting unauthorized changes with an explicit message.
- Separate non-admin image model preference saves from global toggle updates.
- Track preference cleanup results on admin save in imagePreferences.js, propagating cleanup failures instead of ignoring them.
- Require valid preference keys on 403 fallback so toggle-only attempts reject cleanly.
- Add /api/prefs route mock to test_image_model_settings_sync_both_directions_and_survive_reopening.
- Add regression coverage for admin cleanup failures and non-admin toggle rejection.

* fix(settings): reconcile admin toggle on partial cleanup failure and report errors

- In imagePreferences.js, distinguish pre-persistence failures from post-save cleanup failures using err.globalSaved and err.phase.

- In imagePreferences.js, notify listeners of global policy updates on partial success before propagating cleanup failure.

- In imageModels.js, retain the persisted target state on admin toggle when global persistence succeeds but cleanup fails.

- In imageModels.js, reconcile admin toggle with authoritative server state via readImagePreferences() after ambiguous or partial failures.

- In imageModels.js, report partial success accurately without suppressing preference cleanup error messages.

- Add regression coverage in test_settings_save_response_ok_js.py for cleanup HTTP 500, cleanup network rejection, successful cleanup, and failed global POST.

---------

Co-authored-by: pewdiepie-archdaemon <pewdiepie-archdaemon@users.noreply.github.com>
Co-authored-by: Alexandre Teixeira <general@alexandreteixeira.dev>
2026-11-01 02:56:46 +00:00
5353241743 fix(chat): warn before regenerate, resend or edit deletes later messages; offer fork (#6225)
* fix(chat): warn before "regenerate" permanently deletes later messages

Both "regenerate" entry points — the per-message footer's ↻ "Regenerate
from here" button, and the vision editor's "Regenerate message" button —
call POST /api/session/{id}/truncate, which does a real, permanent SQL
delete of every message after the point clicked, then generates a new
reply. Clicking either on anything but the very last exchange did this with
zero warning: the button's own label doesn't say it also erases the rest
of the conversation, and neither button visually signals it's destructive
differently from any other message-footer action.

Caught live: a mis-click destroyed a real conversation with no way to
recover it (no backup covered the affected window). This adds a
confirmation prompt — via the existing uiModule.styledConfirm — whenever
the action would delete more than just the message(s) being regenerated,
stating the exact count of messages that would be lost. Regenerating the
most recent exchange (the everyday case, nothing exists after it) is
unaffected and still proceeds with zero friction.

New test: tests/test_regenerate_truncate_warning_js.py — drives the real
regenerateFrom/resendUserMessage functions under Node with a fake message
list DOM, confirming: the last-exchange case never prompts and truncates
directly; an earlier case prompts with the correct count; cancelling
aborts before any network call; confirming proceeds with the correct
keep_count. Also fixed a pre-existing, unrelated gap this surfaced while
verifying the full suite: test_pr6020_browser_review_regressions.py's
Node harness never stubbed `navigator` at all (sessions.js reads
navigator.platform at module load) — a gap on origin/dev itself, not
caused by this branch's diff, using the same Object.defineProperty fix
already applied on #6019/#6148 for the sibling issue there (Node 21+'s own
getter-only navigator global, not relevant to this specific failure but
the same defensive pattern).

Verified: full pytest suite in-container, 5798 passed / 11 pre-existing
unrelated failures (README/docs/agent-loop baseline) / 0 new.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test(chat): cover plain resend after dev's replace-by-default change

dev made plain resend (no options) replace from the clicked turn by
default, with append-only resend opt-in via { append: true } (see
test_resend_message_nondestructive.py). The footer "Resend message"
button calls resendUserMessage(el) with no options, so it now truncates
everything after the clicked turn and goes through this PR's warning.
Replace the old "plain resend never prompts" test, which assumed the
previous append-by-default behavior, with one asserting that plain
resend on an earlier pair prompts with the right count, and one
asserting that explicit append-only resend never truncates or prompts.

Also resolve ui.js/sessions.js through the same cache-busting query
string chat.js imports them with. ES modules cache by full URL, so the
harness's unversioned import of ui.js was a second module instance and
its uiMod.styledConfirm stub never reached the instance chat.js calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chat): warn before an edit deletes later messages; offer to fork instead

Sending an edited user message truncates the chat at that message, the
same unrecoverable server-side delete as regenerate and resend, but did
it with no prompt. It now goes through the same confirmation, worded for
the action ("Sending this edit will also permanently delete the N
messages after it"). Cancelling leaves the editor open with the draft.

The confirmation for all three actions (regenerate, resend, edit) also
offers "Fork from here": it forks the chat just before the user turn,
switches to the new chat, and replays the action there (the same message,
or the edited text), leaving the original chat untouched. forkFrom now
shares the same fork helper.

Tests drive the real editUserMessage Send handler under Node and cover
the edit prompt (count, cancel, last-message no-prompt) and the fork
choice for each action (fork keep_count, no truncate, replayed text).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chat): cut and fork a chat by message id, not loaded-page position

Regenerate and edit sent keep_count, the clicked message's position among
the messages loaded in the page. A long chat loads only its newest messages
until the user scrolls up, so that position is smaller than the real one and
the truncate deleted real messages above the clicked one (regenerating the
latest reply in a ~50-message chat removed about a dozen earlier messages).
Resend already sent before_msg_id; regenerate and edit now do the same.

The fork options this PR adds had the same flaw. Fork now accepts
before_msg_id (fork before that message) and through_msg_id (include it),
resolved against the stored messages; keep_count stays as the fallback for a
message that has no id yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chat): keep a regenerated turn's message id current; stop on a refused cut

Regenerate keeps the original user bubble on screen and resubmits its text
without drawing a new bubble. The resubmitted message is a new DB row, but
the bubble kept the deleted row's id, so the next regenerate's cut was
refused (404) and the page sent the turn anyway, stacking a second reply.

The stream now reports the saved user message's id (user_message_saved),
which the page attaches to the user bubble, or to the bubble a regenerate
kept. Regenerate and edit stop with an error when the server refuses the
cut instead of sending.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(chat): name the buttons in words instead of symbols

CONTRIBUTING asks for no Unicode emoji in UI or code; the test comments
used the regenerate and pencil glyphs to name buttons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(chat): share the fork-and-send step across edit, resend and regenerate

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chat): pass the regenerate fork its attachment ids explicitly

Resend already hands its collected file ids to _forkAndSend; regenerate
relied on the earlier _pendingRegenAttachments assignment. Pass _regenIds
too so both fork paths read the same, and check the hand-off in the
regenerate tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(chat): assert fork choices keep the user message's attachments

Run regenerate and resend's "Fork from here" against a user message with
an uploaded file and check that the send fired in the fork carries its
file id, replacing the source-text check. chat.js exposes a read-only
_pendingRegenAttachmentIds() for this, like cookbook-hwfit.js's test hooks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(chat): preserve attachments across edit and regeneration

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Alexandre Teixeira <general@alexandreteixeira.dev>
2026-11-01 02:47:32 +00:00
red person 72e8520fa3 fix(models): honor visible pinned IDs in route selection (#6922) 2026-10-31 13:51:59 +00:00
red person 9276db9c11 fix(models): skip non-chat models when resolving chat defaults (#6907) 2026-10-31 13:49:26 +00:00
GrzegorzandClaude Opus 5.5 a797cee0b3 fix(cookbook): diagnose vLLM serves on an NVIDIA driver that is too old (#6727) (#6728)
- Add a diagnosis for "NVIDIA driver on your system is too old" and
  "CUDA driver version is insufficient for CUDA runtime version", the
  errors PyPI's CUDA 13 torch/vLLM builds hit on drivers older than 580.
- Remove the nested _diagnose_serve_output copy in cookbook_routes.py.
  It shadowed the helper and had drifted, so /api/cookbook/tasks/status
  never used the helper's newer diagnoses.
- Capture the tmux pane with -J in the status poll so lines wrapped at
  the pane width do not split error text mid-word.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 13:47:30 +00:00
Grzegorz 76061498c8 fix(gallery): validate album rename and image_ids input (#6822) (#6823) 2026-10-31 13:45:02 +00:00
Merajul Arefin 04f8546d9d fix(search): gallery library per-word search with LIKE escaping (#6584)
Gallery search built a single `%{search}%` LIKE, so multi-word queries only matched the exact adjacent phrase ("red car" missed "a car that is red") and literal % / _ acted as SQL wildcards. Split on whitespace and require each token to match (prompt OR tags OR ai_tags), escaping LIKE wildcards per token; same escaping for the tag filter. Promote _escape_like to public escape_like_pattern() shared helper.

Part of #2123.

Tests: tests/test_gallery_search_multiword.py (5 tests); 4 fail pre-fix, 5 pass post-fix. Neighbors (session_search, gallery null-user, shims): 25 passed.
2026-10-31 13:43:20 +00:00
Robert Vind-Gardoș 9835c1a7f9 fix(skills): refuse renaming a skill onto a name the owner already uses (#6941)
update_skill only checked whether the target directory existed. A skill
with that name in another category lives elsewhere on disk, so the
rename went through and left two skills with one name. Update and
delete look skills up by name, so the next delete removed whichever
file os.walk reached first.

Check the owner's skills in every category before renaming, and answer
409 from PUT /api/skills/{id} instead of the misleading 404.
2026-10-31 13:34:05 +00:00
red person 70fc58225c fix(documents): treat library search punctuation literally (#6944) 2026-10-31 13:21:10 +00:00
red person 9efedc8b07 fix(documents): retain language extensions in ZIP exports (#6848) 2026-10-31 13:18:58 +00:00
Aashish Mahato 9a1fdb192e fix(cookbook): resolve NameError in hf-gguf-files exception handler (#6341) (#6369)
In routes/cookbook_routes.py, the exception handler for GET /api/cookbook/hf-gguf-files referenced repo instead of the parameter repo_id, causing an unhandled NameError and 500 status when upstream requests to Hugging Face failed.

Fixes #6341.
2026-10-31 12:59:17 +00:00
Alexandre Teixeira 1e57f774d3 Merge pull request #6913 from redpersongpt/codex/font-compound-style-family
fix(fonts): group compound weight and style variants by family
2026-10-30 19:44:07 +00:00
Alexandre Teixeira 2b310010cf Merge pull request #6930 from redpersongpt/codex/stale-hf-cache-markers
fix(cookbook): ignore empty markers beside completed blobs
2026-10-30 19:43:50 +00:00
Alexandre Teixeira 412789051e Merge pull request #6585 from robyroro/codex/notes-clear-fields
fix(notes): persist removal of optional note fields
2026-10-30 19:43:01 +00:00
Alexandre Teixeira b8adc4eb60 Merge pull request #6904 from dxrpl/fix/calendar-config-object-body
fix(calendar): answer 400 when a CalDAV config body is not a JSON object (#6903)
2026-10-30 19:42:45 +00:00
Alexandre Teixeira 2246215b79 Merge pull request #6906 from dxrpl/fix/more-validation
fix(api): answer 400 for a malformed body on integrations and document export-zip (#6905)
2026-10-30 19:42:28 +00:00
Alexandre Teixeira 00ecf1c74e Merge pull request #6819 from dxrpl/fix/6818-image-routes-bad-input
fix(gallery): answer 400 for a missing or invalid image on the image-edit routes (#6818)
2026-10-30 19:01:11 +00:00
Alexandre Teixeira 352ee706f3 Merge pull request #6825 from dxrpl/fix/6824-cron-five-fields
fix(tasks): refuse cron expressions with a seconds field (#6824)
2026-10-30 18:58:03 +00:00
Alexandre Teixeira 1aff71836b Merge pull request #6611 from robyroro/fix/chat-tidy-preserve-content
fix(chats): preserve saved conversations during tidy
2026-10-30 15:09:09 +00:00
Alexandre Teixeira 6037707430 fix(cookbook): launch validated llama-server candidate and handle PATH precedence
Ensure the native candidate that passed the health check is prioritized and executed when an earlier broken candidate exists on PATH.

Reconcile Odysseus-owned shims safely, invalidate command hash cache, and verify launchability rather than command existence before serve dispatch.

Fixes #6079
2026-10-30 13:15:40 +00:00
Alexandre Teixeira 5c542d03ac Merge remote-tracking branch 'origin/dev' into fix/pr-6080-llama-recovery 2026-10-30 13:02:47 +00:00
Alexandre Teixeira 59cca6b915 fix(upload): canonicalize confinement on symlinked upload roots 2026-10-29 02:02:47 +00:00
Alexandre Teixeira 571ac09cea fix(security): sanitize file_id and enforce strict prefix confinement on upload paths 2026-10-29 01:39:41 +00:00
Alexandre Teixeira f979e5602f fix(upload,sw): sanitize and confine upload paths and align service worker precache token 2026-10-29 01:35:07 +00:00
Alexandre Teixeira b3e2e628ab fix(upload,ui): confine thumbnail paths and align stylesheet cache-bust token 2026-10-29 01:25:07 +00:00
Alexandre Teixeira 72ba37af6b merge(dev): integrate reviewed preview UI changes 2026-10-29 01:12:08 +00:00
Alexandre Teixeira 6d102a213f fix(security): confine teardown paths and avoid interpolating exception data in logs
- routes/gallery/gallery_routes.py: use stable diagnostic log message without
  interpolating raw exception, resolver details, tokens, or destination strings.
- tests/e2e/teardown.js: remove environment-variable path overrides from production
  code, strictly confine test options and temp directories, and prevent arbitrary
  directory cleanup.
- Add regression tests covering non-leakage of synthetic tokens/queries/exceptions
  into logs and responses, and path confinement during teardown.
2026-10-28 21:14:43 +00:00
Alexandre Teixeira de8f151632 fix(security): harden trace teardown and gallery error responses
- CodeQL #1202 (tests/e2e/teardown.js): replace execSync shell execution with
  execFileSync, passing python executable and sanitizer arguments separately with
  shell: false. Preserve ODYSSEUS_TEST_PYTHON support, timeout handling, and
  fail-closed deletion.
- CodeQL #1203 (routes/gallery/gallery_routes.py): prevent exception information
  exposure in /api/image/edit-progress by returning a generic rejection response
  without leaking internal hostnames, IPs, resolver errors, or credentials.
- Add regression tests covering paths with spaces, metacharacters, synthetic command
  substitutions, process failure modes, fail-closed artifact purge, and SSRF
  rejection confidentiality.
2026-10-28 21:10:12 +00:00
Alexandre Teixeira 2e1c2a343a merge(dev): reconcile photo editor security and upstream changes
- Reconcile conflict resolution in routes/editor_agent_routes.py and src/editor_agent_model.py
- Enforce strict server-authorized endpoint resolution and reject client URL overrides
- Enforce bearer-token privilege separation: delegated API tokens are capped at non-admin scope per src/tool_security.py and cannot inspect private base URLs or probe metadata
- Preserve chat-scope authorization, missing-session 401 rejection, and owner-scoped browser bridge/SSE queues
- Preserve administrator-disabled-tool denylist enforcement introduced by PR #6357
- Fix mobile toolbar flyout hit-testing and stacking context trapping on viewports <= 700px via scoped elevation and mask suppression
- Stabilize project recovery test fixture by awaiting document draft quiescence prior to baseline capture
2026-10-28 19:30:08 +00:00
Alexandre Teixeira c237a1cb9c feat(editor): harden photo editor security and interactions 2026-10-28 17:37:10 +00:00
dxrpl 8acaec20ee fix(gallery): answer 400 for a missing or invalid image on the image-edit routes (#6818) 2026-10-28 18:25:04 +01:00
57a1859f08 fix(security): stop the agent from re-enabling tools an admin disabled (#6357)
* fix(security): stop the agent from re-enabling tools an admin disabled

settings.json:disabled_tools is the only global tool-permission boundary
the Settings > Agent Tools panel offers, yet manage_settings exposed an
`enable_tool` action that removed entries from it. Any agent turn could
undo the admin's restriction, and because the agent reads untrusted web
pages, emails and documents, a planted instruction could re-enable bash,
email or any other tool that had been switched off.

The denylist can now only be narrowed from a turn: `disable_tool` and
`list_tools` keep working, `enable_tool` is refused with a message that
points the user to Settings > Agent Tools, and the action is dropped from
the function schema, the prompt rule and the tool index so models are not
steered into calling it. The free-form set/delete/reset actions were
already unable to reach `disabled_tools`; a test now pins that.

Fixes #5523

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UAB5sz5srMKR7rbeDFtdMH

* fix(settings): close internal API tool-policy bypass

Refuse global tool-policy replacement from internal-token requests even when an admin owner is impersonated, and block the same write in app_api. Route explicit image re-enable requests to list_tools with a Settings pointer. Keep disabled_tools outside general settings actions even if a default is added later. Remove the retired action from capability metadata. Validation: 573 focused tests passed; the running app rejected handler, generic API and encoded-path internal HTTP attempts while preserving human administrator controls.

* fix(mcp): block agent writes to per-server MCP tool policy

PATCH /api/mcp/servers/{id}/tools only checked require_admin, which accepts
the internal tool token, and app_api still forwarded it, so an agent turn
could re-enable MCP tools an admin had switched off. Refuse the internal
token on that route (same check as POST /api/tools) and add PATCH
/api/mcp/servers to the app_api blocklist, with regression tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCbhLNC7mJsunxqfwwshqn

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Afonso Junior <chatgpt@yack.com.br>
2026-10-27 22:50:35 -06:00
red person 8b3a866899 fix(cookbook): ignore empty markers beside completed blobs 2026-10-28 06:31:50 +03:00
Alexandre Teixeira 4079f12ae6 fix(editor): resolve preview authorization and CI regressions 2026-10-28 00:31:42 +00:00
red person 927985f847 fix(fonts): group compound weight and style variants by family 2026-10-28 00:23:18 +03:00
Alexandre Teixeira 091364f7e6 Merge branch 'dev' into review/pewds-pr6738-terra-20261027
Integrate PR #6739 foundation (backend security, bridge hardening, Python 3.11 test fixture correction) with PR #6738 remediations.
- Reconcile routes/email/email_routes.py harmonizing _LIST_LOCK and _LIST_EPOCH with #6738 cache invalidation and thread-safe mutation.
- Preserve editor-session validation, legacy-browser tool restrictions, image-tool authorization, and email cache concurrency invariants.
2026-10-27 18:12:05 +00:00
dxrpl 91922d854c fix(api): answer 400 for a malformed body on integrations and document export-zip (#6905) 2026-10-27 18:33:23 +01:00
Alexandre Teixeira 0133de6f0e feat(security): apply audited PR #6738 remediations and test hardening
- Enforce vision probe SSRF safe local host validation, SafeProbeBackend TOCTOU elimination, and session owner authorization
- Authorize session titling endpoints against registered owner configuration
- Sanitize sensitive metadata logging from agent loop debug logs
- Add async non-blocking execution for spec router tools and thinking decisions
- Harden email cache invalidation fencing and thread-safe mutation
- Add regression test suites for legacy browser authority, spec contracts, spec router request scoping, and task MCP delivery
2026-10-27 17:25:25 +00:00
dxrpl 641752387b fix(calendar): answer 400 when a CalDAV config body is not a JSON object (#6903) 2026-10-27 18:09:14 +01:00
Alexandre Teixeira d105fbde95 fix(editor): harden bridge isolation and completion semantics 2026-10-27 15:36:17 +00:00
pewdiepie-archdaemon 444b60d14e Merge current dev and refresh PR #6738 from the 7011 integration 2026-10-26 16:21:22 +00:00
pewdiepie-archdaemon 93da30c4af Merge current dev and refresh PR #6739 from the 7011 integration 2026-10-26 16:21:17 +00:00
4d0610a1eb fix(email): guard user-supplied IMAP/SMTP hosts against SSRF (#6355)
* fix(email): guard user-supplied IMAP/SMTP hosts against SSRF

POST /api/email/accounts/test opened raw imaplib/smtplib connections to
whatever host:port the request body named, and POST/PUT /api/email/accounts
and PUT /api/email/config persisted arbitrary hosts for the background
pollers to contact. All of these sit behind require_user/require_owner with
no admin gate, so any authenticated user could make the server connect to
loopback, LAN and cloud-metadata targets (169.254.169.254) and read the
first 200 characters of the remote banner back out of the friendly error.
THREAT_MODEL.md lists email as admin-only; the tool layer honours that but
the HTTP routes did not.

Add check_outbound_host() to src/url_safety.py, a bare-host companion to
check_outbound_url() with the same classification (link-local, multicast,
reserved and unspecified always rejected; private/loopback only with
block_private) plus localhost-name handling and an explicit policy for names
that do not resolve. Wire it into the four email routes via
_check_mail_host(): non-admin owners (and everyone when
EMAIL_BLOCK_PRIVATE_IPS=true) also lose private/loopback targets, admins
keep the local-first LAN/Dovecot setups. Unresolvable names pass through
because the connect that follows fails with the same DNS error.

Regression tests cover the helper's policy table and each route: the test
route rejects before any socket opens, save routes reject before touching
the DB, admins keep loopback unless locked down, and an update that fails
validation leaves the stored row untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TwcwfY2uQMxNRsK3y6xBAp

* fix(email): harden IMAP/SMTP SSRF protection against rebinding, TOCTOU, and unvetted hosts

- P0: Implement safe_create_connection() in src/url_safety.py to resolve hostnames once, validate every returned address against SSRF policy, and connect directly to the pinned IP. This eliminates DNS rebinding and TOCTOU while preserving TLS SNI and certificate hostname verification across IMAP, IMAPS, STARTTLS, SMTP, and SMTP_SSL.
- P1: Ensure unresolvable or deferred hostnames cannot become an unrestricted future connection capability by enforcing runtime SSRF validation fail-closed on every connection, while maintaining compatibility with offline configurations.
- P2: Extend SSRF enforcement to the core connection layer (_open_imap_connection, _send_smtp_message, _imap_connect) using ContextVar owner scoping, protecting pre-existing DB rows, restored configs, background pollers, and scheduled operations.
- P3: Allow admin and single-user access to IPv6 loopback (::1, [::1]) for local mail servers (e.g. Dovecot) by checking loopback before is_reserved, while keeping link-local, cloud metadata (169.254.169.254, 100.100.100.200), and non-admin restrictions strictly enforced.
- Add comprehensive regression and adversarial test coverage in tests/test_email_account_host_ssrf.py.

* fix(email): reject unresolved saved hosts without stdlib monkeypatches

Use the existing explicit policy IMAP/SMTP classes and canonical connect_outbound_tcp helper. Remove process-wide stdlib assignments, context-variable plumbing, redundant aliases and resolver-signature retries. Reject unsafe or unresolved hosts before account/config persistence, keep DNS work off the event loop, return fixed client errors, identify Alibaba metadata accurately and reject invalid TCP ports. Validation: 654 relevant email, URL-safety and CodeQL regression tests passed, 3 skipped. Three unrelated browser checks were excluded after environment failures (missing Playwright browser and Chromium timeout). Running-app checks verified rejected create/update/config requests leave stored data unchanged, administrator LAN configuration still works, and metadata/non-admin loopback are blocked.

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: PR #6029 Reviewer <reviewer@odysseus-review.local>
Co-authored-by: Afonso Junior <chatgpt@yack.com.br>
2026-10-26 09:47:03 -06:00
dxrpl bf3c000d1c fix(tasks): refuse cron expressions with a seconds field (#6824) 2026-10-24 23:41:29 +02:00
IsharakSantoshkumarpuppalaisharak7m
0dbf457c00 fix(security): block SSRF via user-supplied model endpoint URLs (#6316)
* fix: block SSRF via user-supplied model endpoint URLs

* fix(test): stub DNS in loopback SSRF tests for deterministic results

Replace environment-dependent test_loopback_always_rejected with two
deterministic tests using a stub resolver:
- test_loopback_accepted_by_default (local-first, block_private=False)
- test_loopback_rejected_in_strict_mode (block_private=True)

The old test only passed when localhost resolved to ::1 (hit is_reserved),
not because loopback was actually always rejected.

Co-Authored-By: Santoshkumarpuppala

* fix: guard PATCH base_url against SSRF and add ipv6 loopback coverage

---------

Co-authored-by: isharak7m <192635824+isharak7m@users.noreply.github.com>
2026-10-23 13:18:54 -06:00
Raul JuarezandRaul b4ae7ebcc4 feat(agent): expose chat history with existing chat scope (#6443)
Co-authored-by: Raul <9117159+raultcj@users.noreply.github.com>
2026-10-23 13:18:49 -06:00
Alexandre Teixeira 377070ab79 fix(email): propagate preflight safety failure across all mailbox groups
- Propagate preflight HTTPException(status_code=502) regardless of whether other mailbox groups exported messages into the temporary ZIP
- Guarantee temporary file cleanup via SpooledTemporaryFile.close() on preflight failure
- Preserve partial success (HTTP 200) for genuinely missing mail
- Add executable tests for multi-group preflight failure orderings, missing mail, and zero body fetches
2026-10-20 15:36:16 +01:00
Alexandre Teixeira f06ec5d91f Merge branch 'upstream/dev' into fix/pr-6742-email-export 2026-10-20 15:01:55 +01:00
Alexandre Teixeira 057ae92c17 fix(email): support case-insensitive IMAP atoms and portable browser discovery
- Make _uid_from_fetch_meta case-insensitive with re.IGNORECASE matching size extraction
- Remove hardcoded developer home paths from test_email_library_print_browser.cjs
- Implement standard Playwright Chromium discovery with environment variable overrides and actionable launch failures
- Add executable tests for uppercase, lowercase, mixed-case IMAP atoms, malformed metadata, and preflight security
2026-10-20 15:00:13 +01:00
Alexandre Teixeira 75df766358 fix(email): harden RFC822.SIZE preflight validation and ignore duplicate IMAP records
- Require valid RFC822.SIZE preflight metadata for every message before body FETCH
- Fail safely with HTTP 502 without body fetching on NO, malformed, or inconsistent sizes
- Distinguish missing mail (HTTP 404) from bad size preflight (HTTP 502)
- Discard duplicate IMAP response records for already-processed UIDs in batch loop
- Correct print notification toast to "Prepared N emails for printing; M unavailable"
- Add dedicated Playwright browser regression test script to repository test suite
- Add request-level tests for preflight NO, empty, partial, malformed, and duplicates
2026-10-20 14:09:47 +01:00