86d0955794336ef2d052d8b5e1568e6cefe56313
6324
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
86d0955794 | Update inbox_disabled.rb | ||
|
|
d720e013d4 | Merge branch 'develop' into codex/cw-4998-disable-inbox | ||
|
|
2f52af6e2d | Update inbox.rb | ||
|
|
5c00a1601a |
chore(deps): bump dompurify from 3.4.0 to 3.4.11 (#14798)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.4.0 to 3.4.11. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/cure53/DOMPurify/releases">dompurify's releases</a>.</em></p> <blockquote> <h2>DOMPurify 3.4.11</h2> <ul> <li>Fixed an issue with a leaky config for hooks via <code>setConfig</code>, thanks <a href="https://github.com/trace37labs"><code>@trace37labs</code></a></li> <li>Bumped vulnerable development dependencies to arrive at plain 0 with <code>npm audit</code></li> <li>Updated the <code>osv-scanner</code> suppression list as no vulnerable dependencies are left for now</li> <li>Updated up the linting tool-chain and removed now-redundant lint directives</li> <li>Updated the documentation is several spots, README, wiki, etc.</li> <li>Bumped several dependencies where possible</li> </ul> <h2>DOMPurify 3.4.10</h2> <ul> <li>Refactored codebase for clarity: extracted the public type declarations into <code>types.ts</code></li> <li>Decomposed the three largest sanitizer functions into focused helpers</li> <li>Removed duplicated defaults and dead branches, consolidated <code>SAFE_FOR_TEMPLATES</code> scrubbing into single shared path</li> <li>Improved per-node performance by hoisting the mXSS probe regexes and testing <code>textContent</code> before <code>innerHTML</code></li> <li>Added a deterministic micro-benchmark harness (<code>npm run bench</code>) with a <code>--compare</code> mode</li> <li>Reduced CI cost by running the full three-engine browser suite once per PR</li> <li>Refreshed the <code>demos/</code> folder so every demo runs again, and added a SVG-via-<code><img></code> demo</li> <li>Documented the bench and <code>test:happydom</code> scripts in the README</li> <li>Completed the Attack Classes & Bypass History wiki page</li> <li>Bumped several dependencies where possible</li> </ul> <h2>DOMPurify 3.4.9</h2> <ul> <li>Further improved the handling of Trusted Types config options, thanks <a href="https://github.com/offset"><code>@offset</code></a></li> <li>Further improved the handling of <code>IN_PLACE</code> sanitization, thanks <a href="https://github.com/mozfreddyb"><code>@mozfreddyb</code></a></li> <li>Added more test coverage for <code>IN_PLACE</code> and Trusted Types related usage</li> <li>Bumped several dependencies where possible</li> <li>Updated README and wiki with more accurate documentation & attack samples</li> </ul> <h2>DOMPurify 3.4.8</h2> <ul> <li>Cleaned up the repository root, renamed some and removed unneeded files</li> <li>Fixed an issue with handling of Trusted Types policies, thanks <a href="https://github.com/fulstadev"><code>@fulstadev</code></a></li> <li>Fixed the node iterator for better template scrubbing, thanks <a href="https://github.com/IamLeandrooooo"><code>@IamLeandrooooo</code></a></li> <li>Included formerly missing LICENSE-MPL in published npm package, thanks <a href="https://github.com/asamuzaK"><code>@asamuzaK</code></a></li> <li>Bumped several dependencies where possible</li> </ul> <h2>DOMPurify 3.4.7</h2> <ul> <li>Hardened the handling of Shadow Roots when using <code>IN_PLACE</code>, thanks <a href="https://github.com/GameZoneHacker"><code>@GameZoneHacker</code></a></li> <li>Removed a problem leading to permanent hook pollution, thanks <a href="https://github.com/offset"><code>@offset</code></a></li> <li>Refactored the test suite and expanded test coverage significantly</li> </ul> <h2>DOMPurify 3.4.6</h2> <ul> <li>Fixed several issues with DOM Clobbering in <code>IN_PLACE</code> mode, thanks <a href="https://github.com/offset"><code>@offset</code></a> & <a href="https://github.com/Bankde"><code>@Bankde</code></a></li> <li>Hardened the checks for cross-realm <code>IN_PLACE</code> and Shadow DOM sanitization, thanks <a href="https://github.com/offset"><code>@offset</code></a> & <a href="https://github.com/Bankde"><code>@Bankde</code></a></li> <li>Added more test coverage for <code>IN_PLACE</code> and general DOM Clobbering attacks</li> <li>Bumped several dependencies where possible</li> </ul> <h2>DOMPurify 3.4.5</h2> <ul> <li>Fixed a bypass caused by the new HTML element <code>selectedcontent</code> added in 3.4.4, thanks <a href="https://github.com/KabirAcharya"><code>@KabirAcharya</code></a></li> </ul> <p><strong>Note that this is a security release for an issue introduced in 3.4.4 and should be upgraded to immediately.</strong></p> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/cure53/DOMPurify/commit/0cae5187403132f96a6d357649e4b15633fc210a"><code>0cae518</code></a> release: 3.4.11 (<a href="https://redirect.github.com/cure53/DOMPurify/issues/1494">#1494</a>)</li> <li><a href="https://github.com/cure53/DOMPurify/commit/6ee5716f8336989753611beeca364957c0eb0c3e"><code>6ee5716</code></a> release: 3.4.10 (<a href="https://redirect.github.com/cure53/DOMPurify/issues/1478">#1478</a>)</li> <li><a href="https://github.com/cure53/DOMPurify/commit/52102472d46035857c52df19e44285f8a1e102fc"><code>5210247</code></a> release: 3.4.9 (<a href="https://redirect.github.com/cure53/DOMPurify/issues/1459">#1459</a>)</li> <li><a href="https://github.com/cure53/DOMPurify/commit/bcdd8285412dc9c4c149652aed2d712e790d6ccf"><code>bcdd828</code></a> release: 3.4.8 (<a href="https://redirect.github.com/cure53/DOMPurify/issues/1439">#1439</a>)</li> <li><a href="https://github.com/cure53/DOMPurify/commit/ca30f070c360df162a3e3848e80e6fd3c9e74bff"><code>ca30f07</code></a> release: 3.4.7 (<a href="https://redirect.github.com/cure53/DOMPurify/issues/1414">#1414</a>)</li> <li><a href="https://github.com/cure53/DOMPurify/commit/bb7739e5bccec7e1ab3dae3f3e42d02db3acaaae"><code>bb7739e</code></a> release: 3.4.6 (<a href="https://redirect.github.com/cure53/DOMPurify/issues/1394">#1394</a>)</li> <li><a href="https://github.com/cure53/DOMPurify/commit/011b0c78f2a0f57ee54f5fcccb697a46ca6e63ea"><code>011b0c7</code></a> release: 3.4.5 (<a href="https://redirect.github.com/cure53/DOMPurify/issues/1382">#1382</a>)</li> <li><a href="https://github.com/cure53/DOMPurify/commit/5817ad969c15e67dfcd6cb37248d6e9c1553e7c3"><code>5817ad9</code></a> release: 3.4.4 (<a href="https://redirect.github.com/cure53/DOMPurify/issues/1374">#1374</a>)</li> <li><a href="https://github.com/cure53/DOMPurify/commit/520edb0371a9638f9b51f1798051299a250c686b"><code>520edb0</code></a> release: 3.4.3 (<a href="https://redirect.github.com/cure53/DOMPurify/issues/1352">#1352</a>)</li> <li><a href="https://github.com/cure53/DOMPurify/commit/6f67fd396a7b8c64294343999fe607ca1f5299c0"><code>6f67fd3</code></a> Sync/3.4.2 (<a href="https://redirect.github.com/cure53/DOMPurify/issues/1322">#1322</a>)</li> <li>Additional commits viewable in <a href="https://github.com/cure53/DOMPurify/compare/3.4.0...3.4.11">compare view</a></li> </ul> </details> <details> <summary>Install script changes</summary> <p>This version adds <code>prepare</code> script that runs during installation. Review the package contents before updating.</p> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/chatwoot/chatwoot/network/alerts). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sony Mathew <sony@chatwoot.com> |
||
|
|
d59415ff5d | Merge branch 'develop' into codex/cw-4998-disable-inbox | ||
|
|
a4be38f8f3 |
fix(facebook): handle messenger sticker attachment type (#14793)
Facebook Messenger sticker messages no longer break message ingestion. Meta recently changed its webhook payloads so a sticker now arrives as a new `sticker` attachment type (alongside the existing `image` attachment during the transition period). Chatwoot didn't recognise `sticker` as a valid attachment file type, so the webhook job crashed and the message — and any others in the same batch — failed to sync. Stickers now appear in the conversation as a single image, just like before. Fixes https://linear.app/chatwoot/issue/PLA-177 ## How to reproduce 1. Connect a Facebook Page inbox. 2. Send a sticker from Messenger to that page. 3. Before this change: the `Webhooks::FacebookEventsJob` raises `ArgumentError: 'sticker' is not a valid file_type` and the message is dropped. 4. After this change: the sticker shows up in the conversation as a single image attachment. --------- Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
6fcc888aee |
chore(deps): bump oj from 3.16.10 to 3.17.3 (#14809)
Bumps [oj](https://github.com/ohler55/oj) from 3.16.10 to 3.17.3. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/ohler55/oj/releases">oj's releases</a>.</em></p> <blockquote> <h2>v3.17.3</h2> <h2>What's Changed</h2> <ul> <li>Fix stack limits by <a href="https://github.com/ohler55"><code>@ohler55</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/1014">ohler55/oj#1014</a></li> <li>Fix intern.c and fast.c by <a href="https://github.com/ohler55"><code>@ohler55</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/1015">ohler55/oj#1015</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/ohler55/oj/compare/v3.17.1...v3.17.3">https://github.com/ohler55/oj/compare/v3.17.1...v3.17.3</a></p> <h2>v3.17.1</h2> <h2>What's Changed</h2> <ul> <li>Add <code>max_integer_digits</code> limit for legacy integer parsing by <a href="https://github.com/meinac"><code>@meinac</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/1009">ohler55/oj#1009</a></li> <li>fix: remove unsafe exec() in fast.c by <a href="https://github.com/orbisai0security"><code>@orbisai0security</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/1011">ohler55/oj#1011</a></li> <li>Revert "fix: remove unsafe exec() in fast.c" by <a href="https://github.com/ohler55"><code>@ohler55</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/1012">ohler55/oj#1012</a></li> <li>Fix reentrant parser by <a href="https://github.com/ohler55"><code>@ohler55</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/1013">ohler55/oj#1013</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/orbisai0security"><code>@orbisai0security</code></a> made their first contribution in <a href="https://redirect.github.com/ohler55/oj/pull/1011">ohler55/oj#1011</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/ohler55/oj/compare/v3.17.0...v3.17.1">https://github.com/ohler55/oj/compare/v3.17.0...v3.17.1</a></p> <h2>v3.17.0</h2> <h2>What's Changed</h2> <ul> <li>Implement Oj::Parser.safe with configurable JSON safety limits by <a href="https://github.com/meinac"><code>@meinac</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/1007">ohler55/oj#1007</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/ohler55/oj/compare/v3.16.17...v3.17.0">https://github.com/ohler55/oj/compare/v3.16.17...v3.17.0</a></p> <h2>v3.16.17</h2> <h2>What's Changed</h2> <ul> <li>Use fast_memcpy16 to quickly copy up to 16 bytes for tail handling in ARM Neon code and small copies when generating JSON. by <a href="https://github.com/samyron"><code>@samyron</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/998">ohler55/oj#998</a></li> <li>Bump jidicula/clang-format-action from 4.15.0 to 4.17.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/ohler55/oj/pull/999">ohler55/oj#999</a></li> <li>Array hash as json by <a href="https://github.com/ohler55"><code>@ohler55</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/1003">ohler55/oj#1003</a></li> <li>Bump jidicula/clang-format-action from 4.17.0 to 4.18.0 by <a href="https://github.com/dependabot"><code>@dependabot</code></a>[bot] in <a href="https://redirect.github.com/ohler55/oj/pull/1001">ohler55/oj#1001</a></li> <li>Handle unterminated strings in usual parser by <a href="https://github.com/meinac"><code>@meinac</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/1002">ohler55/oj#1002</a></li> <li>Fix read() not handling partial reads for large files by <a href="https://github.com/ursm"><code>@ursm</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/1004">ohler55/oj#1004</a></li> <li>Raise error for incomplete primitive literals by <a href="https://github.com/meinac"><code>@meinac</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/1005">ohler55/oj#1005</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/ursm"><code>@ursm</code></a> made their first contribution in <a href="https://redirect.github.com/ohler55/oj/pull/1004">ohler55/oj#1004</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/ohler55/oj/compare/v3.16.16...v3.16.17">https://github.com/ohler55/oj/compare/v3.16.16...v3.16.17</a></p> <h2>v3.16.14</h2> <h2>What's Changed</h2> <ul> <li>Change first arg to oj_parse_options by <a href="https://github.com/ohler55"><code>@ohler55</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/987">ohler55/oj#987</a></li> <li>Fix illegal instruction error on CPUs without SSE4.2 support by <a href="https://github.com/sebyx07"><code>@sebyx07</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/993">ohler55/oj#993</a></li> <li>Optimize <code>oj_dump_cstr</code> using SSE4.2 and SSSE3. by <a href="https://github.com/samyron"><code>@samyron</code></a> in <a href="https://redirect.github.com/ohler55/oj/pull/973">ohler55/oj#973</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/ohler55/oj/compare/v3.16.13...v3.16.14">https://github.com/ohler55/oj/compare/v3.16.13...v3.16.14</a></p> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/ohler55/oj/blob/develop/CHANGELOG.md">oj's changelog</a>.</em></p> <blockquote> <h2>3.17.3 - 2026-06-04</h2> <ul> <li>Fixed issue in intern.c and fast.c.</li> </ul> <h2>3.17.2 - 2026-05-27</h2> <ul> <li>Fixed multiple issues related to extreme sizes.</li> </ul> <h2>3.17.1 - 2026-05-15</h2> <ul> <li>Fixed "quoted string not terminated" error.</li> </ul> <h2>3.17.0 - 2026-04-19</h2> <ul> <li>A "safe" parser has been added as a variation of the Oj:Parser thanks to <a href="https://github.com/meinac"><code>@meinac</code></a>.</li> </ul> <h2>3.16.17 - 2026-04-12</h2> <ul> <li> <p>Rails optimize for Hash and Array now overrides <code>as_json</code> for those classes. Note that when either is optimized with <code>Oj.optimize_rails</code> the <code>Array.as_json</code> and <code>Hash.as_json</code> will not be called.</p> </li> <li> <p>Add support for the rails encoder <code>:only</code> and <code>:except</code> options.</p> </li> <li> <p>Handle unterminated strings in usual parser (<a href="https://redirect.github.com/ohler55/oj/issues/1002">#1002</a>)</p> </li> <li> <p>Fix read() not handling partial reads for large files (<a href="https://redirect.github.com/ohler55/oj/issues/1004">#1004</a>)</p> </li> <li> <p>Raise error for incomplete primitive literals (<a href="https://redirect.github.com/ohler55/oj/issues/1005">#1005</a>)</p> </li> </ul> <h2>3.16.16 - 2026-03-13</h2> <ul> <li>Not closed arrays and objects are reported corrected in the usual parser.</li> </ul> <h2>3.16.15 - 2026-02-05</h2> <ul> <li>Fixed by putting the ostruct dependency back until a better way is found to conditionally include it.</li> </ul> <h2>3.16.14 - 2026-02-04</h2> <ul> <li> <p>Fixed SSE issue <a href="https://redirect.github.com/ohler55/oj/issues/989">#989</a>.</p> </li> <li> <p>Removed ostruct dependency.</p> </li> <li> <p>Removed generic object JSON gem tests.</p> </li> </ul> <h2>3.16.13 - 2025-12-05</h2> <ul> <li>Fixed rails encoding for Hash and Array subclasses.</li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/ohler55/oj/commit/bbde91a679728f94c4492ebc3683f4fa3309049f"><code>bbde91a</code></a> Fix intern.c and fast.c (<a href="https://redirect.github.com/ohler55/oj/issues/1015">#1015</a>)</li> <li><a href="https://github.com/ohler55/oj/commit/495cc38fc5a02681da2175960d4a667fae48f3c9"><code>495cc38</code></a> Update changelog</li> <li><a href="https://github.com/ohler55/oj/commit/810a57acc7aaacef8410b460559c08762d0eb61a"><code>810a57a</code></a> Update changelog</li> <li><a href="https://github.com/ohler55/oj/commit/ec368dbe936ef0104b782e4b0f67b17d6c7276f7"><code>ec368db</code></a> Fix stack limits (<a href="https://redirect.github.com/ohler55/oj/issues/1014">#1014</a>)</li> <li><a href="https://github.com/ohler55/oj/commit/4587e87e23adc9a4163834dc8c9ba9d7206c6501"><code>4587e87</code></a> Fix reentrant parser (<a href="https://redirect.github.com/ohler55/oj/issues/1013">#1013</a>)</li> <li><a href="https://github.com/ohler55/oj/commit/ea0c3b8fdc006807dfd768606548f6f15639b7dc"><code>ea0c3b8</code></a> Fix compile warnings</li> <li><a href="https://github.com/ohler55/oj/commit/45d13099a64c37e231f6cd32bb126ce0270d81c4"><code>45d1309</code></a> Revert "fix: V-001 security vulnerability (<a href="https://redirect.github.com/ohler55/oj/issues/1011">#1011</a>)" (<a href="https://redirect.github.com/ohler55/oj/issues/1012">#1012</a>)</li> <li><a href="https://github.com/ohler55/oj/commit/93ce414fc0b9b1ad2a19311101b6a443ec666320"><code>93ce414</code></a> fix: V-001 security vulnerability (<a href="https://redirect.github.com/ohler55/oj/issues/1011">#1011</a>)</li> <li><a href="https://github.com/ohler55/oj/commit/d68a31ac5bcaaeeed22fb32aeff35aef467ab8d2"><code>d68a31a</code></a> Add <code>max_integer_digits</code> limit for legacy integer parsing (<a href="https://redirect.github.com/ohler55/oj/issues/1009">#1009</a>)</li> <li><a href="https://github.com/ohler55/oj/commit/babd7a11e78a221f6434c5e9e8b9ede9947243e3"><code>babd7a1</code></a> Remove out of date performance comparisons</li> <li>Additional commits viewable in <a href="https://github.com/ohler55/oj/compare/v3.16.10...v3.17.3">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/chatwoot/chatwoot/network/alerts). </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sony Mathew <sony@chatwoot.com> |
||
|
|
c27a5916bb |
fix: auto enable document auto-sync (#14806)
# Pull Request Template ## Description Auto enables document auto-sync on paid plans fixes: https://linear.app/chatwoot/issue/AI-186/captain-auto-sync-doesnt-turn-on-automatically-on-subscription ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules |
||
|
|
66609f06fd |
fix: Bump Nokogiri to 1.19.4 (#14807)
# Pull Request Template ## Description This updates Nokogiri from `1.19.3` to `1.19.4` so the bundle-audit lint step stops flagging the newly published Nokogiri advisories. Chatwoot's direct Nokogiri usage appears limited to ordinary HTML/XML parsing and selector traversal, but the locked dependency is below the patched floor, so the safe remediation is the patch-level upgrade rather than an advisory override. Fixes [CW-7397](https://linear.app/chatwoot/issue/CW-7397/upgrade-nokogiri-to-1194-for-bundle-audit-advisories) ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? Reference failed build in CI because of bundle audit: https://app.circleci.com/pipelines/github/chatwoot/chatwoot/114392/workflows/8a252bf9-5e58-45fd-af18-a32dbebe978b/jobs/160423 - `bundle exec bundle audit update && bundle exec bundle audit check -v` passed with no vulnerabilities found. - `bundle exec rspec spec/services/website_branding_service_spec.rb spec/presenters/html_parser_spec.rb spec/enterprise/services/enterprise/website_branding_service_spec.rb spec/enterprise/services/captain/tools/simple_page_crawl_service_spec.rb` passed with 27 examples and 0 failures. - `bundle exec rubocop app/services/website_branding_service.rb app/presenters/html_parser.rb enterprise/app/services/page_crawler_service.rb enterprise/app/services/captain/tools/html_page_parser.rb enterprise/app/services/captain/tools/simple_page_crawl_service.rb` passed with no offenses. - `git diff --check` passed. Note: broad `bundle exec rubocop --parallel` still reports existing generated DB/schema offenses unrelated to this lockfile-only dependency bump. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules |
||
|
|
6a262287d2 |
feat(captain): allow agents to report Captain messages (#14799)
Adds a cloud-only flow for agents to flag incorrect or problematic Captain (AI) responses. Right-clicking a Captain message surfaces a "Report message" option that opens a dialog to pick a problem type and add a description, persisted to a new captain_message_reports table for the team to review. <img width="636" height="542" alt="Screenshot 2026-06-20 at 9 15 56 AM" src="https://github.com/user-attachments/assets/afaa233d-6bd6-455a-8a33-a3796a3e3ef6" /> <img width="580" height="502" alt="Screenshot 2026-06-20 at 9 16 03 AM" src="https://github.com/user-attachments/assets/2d220d99-98cc-4c5e-a325-778ceb4f7bc9" /> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
dfd656a7d9 |
feat(account): persist signup attribution (#14761)
This keeps Chatwoot-side attribution persistence intentionally small and Enterprise-only. The website owns attribution capture, normalization, and source classification. Chatwoot Cloud only reads the already-shaped first-party attribution cookies during the current web signup account creation path and stores the decoded payload in internal account metadata. Self-hosted installs remain unchanged in this repo. ## What changed - Added Enterprise-only attribution persistence to the current Cloud web signup account creation path. - Stores attribution only when `ChatwootApp.chatwoot_cloud?` is true. - Reads the existing first-touch and last-touch attribution cookies. - Saves only the documented scalar attribution fields under account internal metadata. - Preserves raw attribution values and leaves escaping to display boundaries. - Bounds stored attribution values to the website field-size limit. - Keeps OSS controller code unchanged. - Keeps signup attribution request coverage in Enterprise specs. - Avoids backend attribution derivation, referrer parsing, or fallback classification. - Skips authenticated add-workspace flows so additional workspaces are not counted as signup attribution. - Does not hook unused account creation paths or OmniAuth account creation. ## How to test - `bundle exec rubocop spec/controllers/api/v1/accounts_controller_spec.rb spec/enterprise/controllers/api/v1/accounts_controller_spec.rb enterprise/app/controllers/enterprise/api/v1/accounts_settings.rb enterprise/app/services/internal/accounts/marketing_attribution_service.rb spec/enterprise/services/internal/accounts/marketing_attribution_service_spec.rb` - `bundle exec rspec spec/controllers/api/v1/accounts_controller_spec.rb spec/enterprise/controllers/api/v1/accounts_controller_spec.rb spec/enterprise/services/internal/accounts/marketing_attribution_service_spec.rb` - On a cloud-like setup, create an account through the current web signup path with attribution cookies and confirm account internal metadata is populated. - On a non-cloud setup or authenticated add-workspace flow, confirm account-create behavior is unchanged and no attribution is stored. |
||
|
|
b02e732dd1 |
fix: throttle DELETE conversation API per account (CW-7356) (#14747)
## Description A single account deleted ~167,000 conversations via the API in 60 minutes (avg 46 rps, peak 151 rps/min) on 15 Jun 2026 21:00 to 22:00 IST. The cascade through `dependent: :destroy_async` on conversations, then messages, then reporting_events, created enough job queue and DB pressure to drive RDS CPU to ~93%, triggering 5xx alerts and connection-pool timeouts. This adds a `Rack::Attack` per-account throttle on `DELETE /api/v1/accounts/:id/conversations/:id`: - Default 60 req/min per account; configurable via the `RATE_LIMIT_CONVERSATION_DELETE` env var. Fixes [CW-7356](https://linear.app/chatwoot/issue/CW-7356) |
||
|
|
80b132e155 |
feat(tiktok): add cloud warning (#14768)
Adds Cloud-only warning on the TikTok connect page while North American account connections may be temporarily unavailable. Fixes https://linear.app/chatwoot/issue/CW-7322/cloud-show-tiktok-north-america-warning-on-connect-page --------- Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com> |
||
|
|
32b63ad0c0 | feat: add search to Help Center admin pages (#14772) | ||
|
|
ac15339456 |
fix: Resolve Firefox input issues and persist advanced filters (#14781)
# Pull Request Template ## Description This PR fixes a few issues in Global Search and removes a non-functional control. * Fixes an issue in Firefox where characters could be dropped while typing in the search input. The search now uses the latest input value directly, preventing searches from running one character behind. * Removes a stale query sync in `SearchHeader` that could overwrite recently typed characters during the debounce window, causing the input to appear out of sync. * Fixes advanced search filters being removed from the URL on page reload. The search page now waits for account data to load before parsing URL parameters, ensuring agent, inbox, and date range filters are preserved. * Removes the non-functional "Sort by relevance" button from the search tabs bar, as it was disabled and had no effect. Fixes https://github.com/chatwoot/chatwoot/issues/14684 [CW-7305](https://linear.app/chatwoot/issue/CW-7305/global-search-drops-characters-while-typing-in-firefox-query-truncated) [CW-7370](https://linear.app/chatwoot/issue/CW-7370/remove-non-functional-relevance-placeholder-button-from-global-search) ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? ### Screencast **Before** https://github.com/user-attachments/assets/48d72a4e-20f4-4f24-91f4-2c9a9c065eba **After** https://github.com/user-attachments/assets/0e41a803-b4fd-410d-a739-549f72d418d6 ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules |
||
|
|
f99187646e | Merge branch 'release/4.15.1' into develop | ||
|
|
5ce6e00730 | Bump version to 4.15.1 | ||
|
|
f66b551c7d |
revert: Sidebar unread counts for filters (CW-7262) (#14769)
## Description Reverts [#14726](https://github.com/chatwoot/chatwoot/pull/14726) (\"feat: Add sidebar unread counts for filters (CW-7262)\"), which shipped in 4.15.0. After 4.15.0 rolled out to prod the unread-counts-for-filters code path caused a cascading incident: - `Counter#ensure_filters_cache!` fires on every `/unread_counts/index` and `update_last_seen` request. - On cache miss it calls `Builder#build_filters_for!`, which: - invokes `store.clear_user_filters!` -> `delete_matching` -> a Redis `SCAN_each` over a per-user pattern keyspace, and - runs 4 fresh SQL passes per user (mentions, participating, unattended, and per-folder `Conversations::FilterService` queries). - Threads blocked in the SCAN held their DB connections, the connection pool exhausted, Sidekiq jobs were discarded with `ActiveJob::DeserializationError: could not obtain a connection from the pool`, and the enqueued queue blew past 200K. Related: [CW-7262](https://linear.app/chatwoot/issue/CW-7262/unread-counts-for-filters-folders) ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules |
||
|
|
c12e1f8348 | Merge branch 'release/4.15.0' into develop | ||
|
|
124782938d | Bump version to 4.15.0 | ||
|
|
c70a57407d |
fix: label Chatwoot Mobile sessions instead of 'Unknown Device' (#14753)
## Description Sessions created by logins from the Chatwoot Mobile app currently render as "Unknown Device" in the dashboard sessions UI. The mobile app's HTTP layer sends: - Android: `User-Agent: okhttp/4.9.2` - iOS: `User-Agent: Chatwoot/<build> CFNetwork/<v> Darwin/<v>` Neither pattern is classifiable by the `browser` gem, so \`browser_name\`, \`platform_name\`, and \`device_name\` all end up "Unknown". This change adds a backend-only fallback in \`UserSessionTrackingService\`. When \`Browser.new(ua)\` returns "Unknown Browser" and the UA matches a known Chatwoot Mobile pattern, the labels are overridden to \`Chatwoot Mobile\` + \`Android\` / \`iPhone\`. The Vue sessions list (\`ActiveSessions.vue\`) then renders "Chatwoot Mobile on Android" or "Chatwoot Mobile on iPhone" with the smartphone icon. This is the immediate floor. A follow-up will add structured \`X-Chatwoot-*\` headers from the mobile app so we can render full version + device model. Fixes INF-75. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? - Added specs ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules |
||
|
|
fe6368b42e |
feat: Add sidebar unread counts for filters (CW-7262) (#14726)
## Description Extends the conversation unread-count system so the left sidebar can show unread badges for Mentions, Participating, Unattended, and saved conversation folders. Folder badges reuse the existing `custom_filters` conversation filter semantics, store user-scoped Redis sets lazily, and skip unsupported folder filters so invalid saved folders continue to render without a badge. The Unattended badge counts all visible unread open conversations that match the existing unattended conversation scope. Closes - [CW-7262](https://linear.app/chatwoot/issue/CW-7262/unread-counts-for-filters-folders) ## What changed - Added user-scoped unread-count Redis keys and cache builders for mentions, participating conversations, unattended conversations, and saved folder filters. - Reused `Conversations::FilterService` through a relation-returning path so folder counts match the folder conversation list behavior. - Invalidated user filter caches from mention, participant, custom-filter, and relevant conversation update events. - Extended the unread-count endpoint payload and sidebar Vuex/sidebar rendering for the new badge counts, including the Unattended sidebar item. - Added Ruby, Enterprise, request, listener, and frontend store coverage for the new unread-count dimensions. ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? - Created local validation folders for `john@acme.inc` and confirmed the unread-count payload includes open, resolved, and high-priority folder badges while excluding the invalid unsupported folder. - Added coverage for the Unattended badge rule: all visible unread open conversations matching `Conversation.unattended`. - Ran focused unread-count Ruby specs, including service, listener, request, and Enterprise counter coverage. - Ran frontend unread-count store specs. - Ran RuboCop on the touched Ruby files. - Ran ESLint through the project script; it completed with warnings in existing unrelated files and no errors. <img width="369" height="525" alt="Screenshot 2026-06-13 at 10 51 39 PM" src="https://github.com/user-attachments/assets/36b1d2c4-dac1-4f6f-9c0e-7ef5a6cc2975" /> ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [x] Documentation changes are not required for this internal unread-count behavior - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] No dependent downstream changes are required --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> |
||
|
|
352f120c6a |
feat: add dedicated color tokens for voice call widget (#14745)
# Pull Request Template ## Description This PR introduces dedicated color tokens for the floating voice call widget instead of reusing generic design tokens. This allows the widget to maintain a distinct dark-card appearance across both light and dark themes. Adds the following tokens and applies them to `CallCard`: * `--call-widget` * `--call-widget-border` * `--call-widget-text` * `--call-widget-sub-text` Fixes https://linear.app/chatwoot/issue/CW-7353/call-notification-window-theme-update-design ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? ### Screenshots **Light mode** <img width="1466" height="815" alt="Screenshot 2026-06-16 at 3 47 32 PM" src="https://github.com/user-attachments/assets/732164b1-5488-4cc1-8c71-01245d906842" /> **Dark mode** <img width="1466" height="815" alt="Screenshot 2026-06-16 at 3 46 54 PM" src="https://github.com/user-attachments/assets/536ab632-4c7c-486b-8fc4-a061ee40b22a" /> ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules |
||
|
|
a585853690 |
chore: expose advanced_search flag in super_admin UI for self-hosted (#14750)
## Description Removes `chatwoot_internal: true` from the `advanced_search` feature flag so self-hosted enterprise userss can toggle it from the super_admin UI instead of going through a rails console. |
||
|
|
64b0ebd8dc |
feat: Script to migrate images between providers (#9117)
# Pull Request Template ## Description Storage migration functionality, which allows the transfer of images from one on-premises provider to another (e.g. AWS, Google, etc.) using Active Storage. I ran the unit and integration tests and they all passed correctly. **The command to execute the migration is `FROM=from_service TO=to_service rake storage:migrate`** Fixes #7907 ## Type of change Please delete options that are not relevant. - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? I have tested the storage migration functionality by running the provided unit and integration tests. Additionally, I manually tested the migration process in a local development environment by following these steps: Set up two storage services (e.g., AWS S3 and Google Cloud Storage) with valid configurations. Execute the `FROM=local TO=amazon rake storage:migrate` task with appropriate FROM and TO arguments to migrate blobs between the services. Verified that the blobs were successfully transferred to the target storage service. Checked for any error messages or unexpected behavior during the migration process. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Sony Mathew <sony@chatwoot.com> Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com> |
||
|
|
cf6ee13665 |
feat: scale email rate limits by agent count for paid plans (#13966)
Paid plan accounts with many agents were hitting the flat daily email rate limit cap. This multiplies the plan base limit by the account's agent seat count, so larger teams get proportionally higher limits. Free/hacker plan keeps the flat limit unchanged. Fixes https://linear.app/chatwoot/issue/CW-6664 ## How to test 1. Set up `ACCOUNT_EMAILS_PLAN_LIMITS` config with plan limits (e.g., `{"hacker": 10, "startups": 20, "business": 30, "enterprise": 40}`) 2. Create an account on a paid plan (e.g., startups) with multiple agent seats 3. Verify `account.email_rate_limit` returns `base_limit × agent_seats` (e.g., 20 × 5 = 100) 4. Create an account on the hacker plan — verify limit stays flat (10) 5. Set a per-account override via super admin `limits.emails` — verify it takes priority over the multiplied limit ## What changed - `plan_email_limit` now multiplies the base plan limit by agent seat count for paid plans - Added `free_plan?` helper to skip the multiplier for the default/free plan |
||
|
|
41a3ab6dfa |
feat(companies): add contact company selector (#14496)
Adds a company selector to the contact details form so agents can associate a contact with an existing company directly from the contact page. Closes - None Why Contacts already expose company information through the CRM fields, but the form only accepted free-text company names. As we split company CRM work into smaller PRs, this keeps the contact page aligned with the structured company model while preserving the existing company-name behavior used by automations. What changed - Shows a company dropdown in the contact details form when the Companies feature is enabled. - Keeps legacy free-text company names editable when a contact has no structured `company_id`. - Allows Enterprise contact create/update APIs to accept account-scoped `company_id`. - Syncs `additional_attributes.company_name` when a contact is associated with a company, including the existing email-domain auto-association path. - Serializes `company_id` in the contact model payload so the form can show the current association. How to test 1. Enable Companies for an account and open a contact details page. 2. In Edit contact details, use the Company field to select an existing company. 3. Save the contact and refresh the page. 4. Confirm the selected company remains visible and the contact is associated with that company. 5. Confirm contacts with only a legacy free-text company name still show the text input instead of an empty selector. --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin <iamsivin@gmail.com> Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com> |
||
|
|
de137e8297 |
feat: Capture CTWA referral metadata for WhatsApp conversations (#14681)
Adds support for capturing Click-to-WhatsApp ad referral metadata from incoming WhatsApp messages. This stores Meta Cloud API `referral` payloads on the incoming message `content_attributes` and normalizes Twilio `Referral*` callback fields into the same shape. The UI display is intentionally deferred until Instagram, Messenger, and TikTok referral payloads are captured as well, so we can design one cross-channel referral surface instead of a WhatsApp-only sidebar block. Why message-level storage Meta sends CTWA referral details as part of the inbound message payload, not as a stable conversation-level webhook. The referral represents the exact ad click that produced that specific customer message, and later messages in the same conversation may not carry the same context. Storing the normalized referral on the message preserves the original webhook semantics, avoids adding conversation-level attribution that could become stale or ambiguous, and keeps support for both Cloud API and Twilio payloads aligned behind `content_attributes.referral`. Fixes https://linear.app/chatwoot/issue/CW-7090/surface-meta-ctwa-referral-on-incoming-whatsapp-messages-cloud-api Closes https://github.com/chatwoot/chatwoot/issues/13995, https://github.com/chatwoot/chatwoot/issues/12560, https://github.com/chatwoot/chatwoot/issues/13006 Related community PRs - https://github.com/chatwoot/chatwoot/pull/13130 - https://github.com/chatwoot/chatwoot/pull/14180 - https://github.com/chatwoot/chatwoot/pull/14121 Related follow-ups - https://linear.app/chatwoot/issue/CW-7301/capture-instagram-ad-referral-metadata-on-incoming-messages - https://linear.app/chatwoot/issue/CW-7302/capture-messenger-ad-referral-metadata-on-facebook-page-conversations - https://linear.app/chatwoot/issue/CW-7303/capture-tiktok-ad-referral-metadata-from-im-referral-msg-events How to test 1. Send or replay a WhatsApp Cloud API inbound message that contains a `messages[0].referral` payload from a Click-to-WhatsApp ad. 2. Confirm the generated incoming message stores the payload under `content_attributes.referral`. 3. Repeat with a Twilio WhatsApp callback containing `Referral*` fields and confirm the stored message has the same normalized `content_attributes.referral` structure. --------- Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com> |
||
|
|
a3d05ef55d | chore: Update emoji picker in widget (#14741) | ||
|
|
dda25c0b51 |
fix(portal): emit valid BCP 47 html lang attribute (#14680)
Locales are stored with underscores (e.g. pt_BR), but the HTML lang attribute requires hyphens (pt-BR). Convert via a helper in the public portal layouts. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> |
||
|
|
1713cabdf2 | feat: Add emoji & icon picker for Help Center categories (#14702) | ||
|
|
d8a16278b9 |
fix: added ordering capability for sidebar sections folders, teams, channels and labels (CW-7193) (#14609)
## Description * Added the ability to sort for 4 sub-sections under conversations folders, teams, channels and labels. * the sort options are basically created at, alphabetical and unread counts along with both directions. * for folders we don't have an unread count, so we sort it by only created and alphabetical. * all the sort preferences are stored on the frontend - easiest implementation for now. Fixes # CW-7193 ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? Tested this locally by visually verifying the changes. Also ran the newly added tests for component level changes. Here is the screenshot of the changes: Added the sort option to sub sections in the conversation sidebar: <img width="282" height="848" alt="Screenshot 2026-06-02 at 1 58 12 AM" src="https://github.com/user-attachments/assets/4a7c6061-86e3-438a-92ae-ee643a0128b6" /> The sort options looks like this: <img width="783" height="698" alt="Screenshot 2026-06-02 at 1 58 49 AM" src="https://github.com/user-attachments/assets/a15bb0a7-b810-4423-a88c-fbd84d0476c0" /> ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin <iamsivin@gmail.com> |
||
|
|
4816e923b6 |
chore: added a new sort by unread option for conversations (CW-7152) (#14512)
## Description Added a new option for the sort by option in conversation filters called unread. This is to filter out unread conversations. Fixes # CW-7152 ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? Tested manually in local. <img width="1397" height="603" alt="Screenshot 2026-05-20 at 10 40 20 PM" src="https://github.com/user-attachments/assets/6c60263e-907b-419f-a7ed-06010bbe8736" /> ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules |
||
|
|
396631ad7d |
feat: enforce concurrent session limit with login picker (CW-7169) (#14621)
## Description Cap active sessions at `MAX_USER_SESSIONS` which defaults to existing value of `25` per user. This ensure existing user login behavior is not affected for self-hosted installations. Browser users at the cap see a session picker (409 response) to choose which session to end. Non-browser clients and partially-tracked users get silent oldest-session eviction. Depends on #14556. ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? Specs cover: under limit, at limit (browser picker, non-browser eviction), partial tracking fallback, revoke single/all sessions during login, session row creation on successful login. --------- Co-authored-by: Sony Mathew <sony@chatwoot.com> |
||
|
|
ba0ba46c9c |
fix: skip session tracking and use short-lived token for impersonation (CW-7169) (#14622)
## Description SuperAdmin impersonation SSO logins no longer create UserSession rows visible to the customer. Impersonation tokens use a 2-day lifespan instead of ~2 months, so they naturally evict first and don't linger in the user's token list. Server-side detection via Redis value (`'impersonation'` vs `'normal'`) without changing the `valid_sso_auth_token?` signature. Backward compatible with in-flight tokens. Depends on #14556. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? Specs cover: impersonation login skips UserSession creation, impersonation token has short lifespan, normal SSO login still creates UserSession row. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules |
||
|
|
ee6382109a |
feat: prevent deleted email conversations from syncing again (#14612)
# Pull Request Template ## Description Prevent deleted email conversations from being synced into Chatwoot again while they are still within the IMAP sync window. When an admin explicitly deletes an email conversation, the incoming email message IDs are stored temporarily in Redis. IMAP sync checks these recently deleted message IDs in addition to existing message records. Each Redis key expires automatically after two days. This applies only to explicit conversation deletion. Individual message deletion, inbox deletion, and account deletion keep their existing behavior. Fixes [CW-7214](https://linear.app/chatwoot/issue/CW-7214/deleted-mails-in-gmail-inbox-gets-synced-again) |
||
|
|
35bef21f83 | feat: add media view for contacts (#14393) | ||
|
|
e5c140158e |
fix(new-conversation): stop writing literal "undefined" as mail_subject (#14383)
## Description This PR fixes a data-corruption bug in the new conversation flow that surfaces in conversation search results as `Subject: undefined`. ### The Problem When creating a conversation through the "New conversation" modal without typing a subject (every non-email channel never shows the subject input, and email channels can be left blank), the conversation gets persisted with `additional_attributes.mail_subject = "undefined"` (the literal string). The bogus value shows up in conversation search results as `Subject: undefined`, which started rendering after #10843. ### Root Cause `createConversationPayload` in `app/javascript/dashboard/store/modules/contactConversations.js` appends the `mail_subject` field unconditionally: ```js payload.append('additional_attributes[mail_subject]', mailSubject); ``` `composeConversationHelper.js` only sets `payload.mailSubject` when `subject` is truthy, so when no subject is provided, `mailSubject` is destructured as `undefined` in `createConversationPayload`. `FormData.append` coerces `undefined` to the string `"undefined"`, and the backend persists it as-is into the JSONB column. ### The Fix Skip the append when `mailSubject` is falsy. The backend already treats a missing key the same as an empty subject (the email mailer falls back to a default subject when `mail_subject` is `nil`), so omitting the field is safe across channels. ### Key Changes - Guarded the `additional_attributes[mail_subject]` append in `createConversationPayload`. - Added a spec covering the case where `mailSubject` is omitted. > Note: existing rows persisted before this fix will continue to show `Subject: undefined` in search until cleaned up. A simple SQL cleanup for non-email inboxes: > > ```sql > UPDATE conversations > SET additional_attributes = additional_attributes - 'mail_subject' > FROM inboxes > WHERE conversations.inbox_id = inboxes.id > AND inboxes.channel_type <> 'Channel::Email' > AND conversations.additional_attributes->>'mail_subject' = 'undefined'; > ``` > > I'm leaving any data-cleanup migration out of this PR since it's a maintenance concern that may be handled differently per installation. ## Type of change - [X] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? 1. Open the "New conversation" modal on a non-email inbox (e.g., WhatsApp, SMS, API). 2. Create a conversation without filling any subject (field is not present on those, so regular flow). 3. Open the global search and search for the conversation. 4. **Before:** the result card shows `Subject: undefined`. **After:** the subject row is hidden. 5. Repeat on an email inbox leaving the subject blank — same result. 6. On an email inbox, type a subject and verify it still persists and renders correctly. 7. Run the new spec: `pnpm test contactConversations`. ## Checklist: - [X] My code follows the style guidelines of this project - [X] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [X] My changes generate no new warnings - [X] I have added tests that prove my fix is effective or that my feature works - [X] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules Co-authored-by: Sojan Jose <sojan@pepalo.com> Co-authored-by: Sony Mathew <sony@chatwoot.com> |
||
|
|
274e92e0e4 |
chore: collapse conversation sidebar sections (folders, teams, inboxes and labels) - CW-7059 (#14509)
## Description Added ability to collapse conversation sidebar sections (folders, teams, inboxes and labels) Fixes #CW-7059 ## Type of change Please delete options that are not relevant. - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected) - [ ] This change requires a documentation update ## How Has This Been Tested? Tested locally. Added specs. Attaching the loom for them same. https://github.com/user-attachments/assets/40d613e7-6c82-4078-abf4-79739a00f718 ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: iamsivin <iamsivin@gmail.com> |
||
|
|
27404b4a27 |
fix: redact sensitive integration secrets from API responses (#14147)
## Summary The `GET /api/v1/accounts/:id/integrations/apps` endpoint returns raw secret values (OpenAI API keys, Google service account private keys, Linear refresh tokens, etc.) in hook settings within the JSON response. Although gated behind an administrator check, these secrets are visible in the browser network tab. This PR filters hook settings through the existing `visible_properties` whitelist defined in `config/integration/apps.yml`, and adds explicit whitelists to integrations that were missing them. Closes #14042 ## Bug reproduction **Setup:** Created an OpenAI integration hook with a fake API key (`sk-test-secret-12345`). **Step 1 — Browser Network tab shows raw secrets:** <img width="1676" height="869" alt="Screenshot 2026-04-24 at 14 32 28" src="https://github.com/user-attachments/assets/6fc69463-365e-458b-b216-98a7390bf528" /> Navigate to Settings > Integrations as an admin. Open DevTools Network tab and observe the response from `GET /api/v1/accounts/:id/integrations/apps`. The hook settings contain the full API key in plaintext: ```json "hooks": [ { "id": 1, "app_id": "openai", "settings": { "api_key": "sk-test-secret-12345", "label_suggestion": false } } ] ``` **Step 2 — API call confirms the leak:** ```bash curl -s "http://localhost:3000/api/v1/accounts/2/integrations/apps" \ -H "access-token: <token>" \ -H "client: <client>" \ -H "uid: user@test.com" \ | jq '.payload[] | select(.id == "openai") | {id, name, hooks: [.hooks[] | {id, app_id, settings}]}' ``` Response: ```json { "id": "openai", "name": "OpenAI", "hooks": [ { "id": 1, "app_id": "openai", "settings": { "api_key": "sk-test-secret-12345", "label_suggestion": false } } ] } ``` Any admin user can extract the raw API key from the response. The same applies to other integrations — Dialogflow exposes full Google service account credentials, Linear exposes refresh tokens, etc. ## After fix After applying this change, the GET /api/v1/accounts/:id/integrations/apps endpoint no longer returns sensitive secret values in hook settings. Instead, the response is filtered using each integration’s visible_properties whitelist, ensuring only safe, user-facing fields are exposed. For example, OpenAI integrations return non-sensitive fields like label_suggestion while excluding raw API keys. This prevents secrets from being exposed in the browser network tab or API responses, even for authenticated admin users. ```bash curl -X GET "http://localhost:3000/api/v1/accounts/2/integrations/apps" \ -H "Accept: application/json" \ -H "Authorization: Bearer eyJhY2Nlc3MtdG9rZW4iOiJqZG5jOWg4TnljaWFJa3JlZkxGQzRnIiwidG9rZW4tdHlwZSI6IkJlYXJlciIsImNsaWVudCI6Ik81bjVnTDFEOVVhbGpwbWxjaHZNanciLCJleHBpcnkiOiIxNzgyMzMyNTA4IiwidWlkIjoidXNlckB0ZXN0LmNvbSJ9" \ -H "access-token: jdnc9h8NyciaIkrefLFC4g" \ -H "client: O5n5gL1D9UaljpmlchvMjw" \ -H "uid: user@test.com" \ | jq '.payload[] | select(.id == "openai") | {id, name, hooks: [.hooks[] | {id, app_id, settings}]}' % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 10174 100 10174 0 0 33232 0 --:--:-- --:--:-- --:--:-- 33357 { "id": "openai", "name": "OpenAI", "hooks": [ { "id": 1, "app_id": "openai", "settings": { "label_suggestion": false } } ] } ``` ## What changed - Added `visible_properties` accessor to `Integrations::App` model to expose the whitelist from config - Updated `_hook.json.jbuilder` to filter `resource.settings` through the associated app's `visible_properties` instead of returning the full hash - Added `visible_properties` to integrations that were missing it: - Linear: `[]` (settings contain refresh_token) - Notion: `[]` (OAuth-based, no user-facing settings) - Slack: `['channel_name']` (UI needs this to display connected channel) - Shopify: `[]` (no settings) App config metadata (`_app.json.jbuilder`) is left unchanged — hook_type, settings_form_schema, etc. are not secrets and the frontend depends on them. ## How to test 1. Create an OpenAI integration hook with an API key 2. As an admin, call `GET /api/v1/accounts/:id/integrations/apps` 3. Verify hook settings include `api_key` (whitelisted) but not raw credential objects 4. For Dialogflow hooks, verify `credentials` (private key JSON) is excluded while `project_id` is included 5. For Slack hooks, verify `channel_name` is still returned 6. For Linear hooks, verify `refresh_token` is not returned --------- Co-authored-by: Botshelo Nokoane (Konstruktors) <botshelo@entersekt.com> Co-authored-by: Sony Mathew <sony@chatwoot.com> Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com> |
||
|
|
006b529918 |
chore(deps): bump net-imap from 0.4.24 to 0.6.4.1 (#14688)
Bumps [net-imap](https://github.com/ruby/net-imap) from 0.4.24 to 0.6.4.1. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/ruby/net-imap/releases">net-imap's releases</a>.</em></p> <blockquote> <h2>v0.5.15</h2> <h2>What's Changed</h2> <h3>🔒 Security</h3> <p>This release fixes several more security vulnerabilities which are related to the fixes in <code>v0.5.14</code>. Please see the linked security advisories for more information.</p> <ul> <li><em>(moderate)</em> Command Injection via non-synchronizing literal in "raw" argument (CVE-2026-47240, GHSA-8p34-64r3-mwg8) This vulnerability depends how the server interprets non-synchronizing literals. The connection is <em>not</em> vulnerable if the server supports non-synchronizing literals. <ul> <li>🥅 Validate non-synchronizing literals support by <a href="https://github.com/nevans"><code>@nevans</code></a> in <a href="https://redirect.github.com/ruby/net-imap/pull/703">ruby/net-imap#703</a> (backports <a href="https://redirect.github.com/ruby/net-imap/pull/701">ruby/net-imap#701</a>)</li> </ul> </li> <li><em>(moderate)</em> Command Injection via unvalidated ID and ENABLE arguments (CVE-2026-47242, GHSA-46q3-7gv7-qmgg) <ul> <li>🥅 Validate <code>ID</code> values contain only valid bytes by <a href="https://github.com/nevans"><code>@nevans</code></a> in <a href="https://redirect.github.com/ruby/net-imap/pull/703">ruby/net-imap#703</a> (backports <a href="https://redirect.github.com/ruby/net-imap/pull/698">ruby/net-imap#698</a>)</li> <li>🥅 Validate <code>#enable</code> arguments are all atoms by <a href="https://github.com/nevans"><code>@nevans</code></a> in <a href="https://redirect.github.com/ruby/net-imap/pull/703">ruby/net-imap#703</a> (backport <a href="https://redirect.github.com/ruby/net-imap/pull/699">ruby/net-imap#699</a>) <strong>NOTE:</strong> <code>#enable</code> should <strong><em>never</em></strong> be called with untrusted input.</li> </ul> </li> <li><em>(low)</em> Denial of Service via incomplete "raw" argument validation (CVE-2026-47241, GHSA-c4fp-cxrr-mj66) This results in the affected command hanging until the connection is closed. If another thread attempts to send a concurrent pipelined command, the first thread will return with a syntax error and the second thread will hang until the connection closes. <ul> <li><em>Reported by <a href="https://github.com/fg0x0"><code>@fg0x0</code></a></em></li> <li>🐛 Prevent trailing <code>{0}</code> in RawData validation by <a href="https://github.com/nevans"><code>@nevans</code></a> in <a href="https://redirect.github.com/ruby/net-imap/pull/703">ruby/net-imap#703</a> (backports <a href="https://redirect.github.com/ruby/net-imap/pull/700">ruby/net-imap#700</a>)</li> </ul> </li> </ul> <h3>Fixed</h3> <ul> <li>🥅 Validate that Atom and Flag are not empty by <a href="https://github.com/nevans"><code>@nevans</code></a> in <a href="https://redirect.github.com/ruby/net-imap/pull/685">ruby/net-imap#685</a> (backports <a href="https://redirect.github.com/ruby/net-imap/pull/684">ruby/net-imap#684</a>)</li> <li>🧵 Fix deadlock in <code>#disconnect</code> by <a href="https://github.com/nevans"><code>@nevans</code></a> in <a href="https://redirect.github.com/ruby/net-imap/pull/697">ruby/net-imap#697</a> (backports <a href="https://redirect.github.com/ruby/net-imap/pull/686">ruby/net-imap#686</a>)</li> </ul> <h3>Documentation</h3> <ul> <li>⚠️ Boost visibility of raw data argument documentation warnings by <a href="https://github.com/nevans"><code>@nevans</code></a> in <a href="https://redirect.github.com/ruby/net-imap/pull/696">ruby/net-imap#696</a> (backports <a href="https://redirect.github.com/ruby/net-imap/pull/677">ruby/net-imap#677</a>)</li> </ul> <h3>Other Changes</h3> <ul> <li>🏷️ Allow 64-bit Integer arguments in <a href="https://redirect.github.com/ruby/net-imap/pull/696">ruby/net-imap#696</a> (backports <a href="https://redirect.github.com/ruby/net-imap/pull/675">ruby/net-imap#675</a>)</li> <li>🥅 Ensure send_number_data input is an Integer in <a href="https://redirect.github.com/ruby/net-imap/pull/696">ruby/net-imap#696</a> (backports <a href="https://redirect.github.com/ruby/net-imap/pull/676">ruby/net-imap#676</a>)</li> <li>♻️ Improve <code>RawData.new</code>, Add <code>RawData.split</code> by <a href="https://github.com/nevans"><code>@nevans</code></a> in <a href="https://redirect.github.com/ruby/net-imap/pull/696">ruby/net-imap#696</a> (backports <a href="https://redirect.github.com/ruby/net-imap/pull/679">ruby/net-imap#679</a>)</li> <li>🥅 Validate response literal byte size format by <a href="https://github.com/nevans"><code>@nevans</code></a> in <a href="https://redirect.github.com/ruby/net-imap/pull/696">ruby/net-imap#696</a> (backports <a href="https://redirect.github.com/ruby/net-imap/pull/681">ruby/net-imap#681</a>)</li> </ul> <h3>Miscellaneous</h3> <ul> <li>✅ Improvements to tests' FakeServer in <a href="https://redirect.github.com/ruby/net-imap/pull/696">ruby/net-imap#696</a> (backports <a href="https://redirect.github.com/ruby/net-imap/pull/678">ruby/net-imap#678</a>)</li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/ruby/net-imap/compare/v0.5.14...v0.5.15">https://github.com/ruby/net-imap/compare/v0.5.14...v0.5.15</a></p> <h2>v0.5.14</h2> <h2>What's Changed</h2> <h3>🔒 Security</h3> <p>This release contains fixes for <strong>multiple vulnerabilities</strong> concerning <em><strong><code>STARTTLS</code> stripping</strong></em>, argument validation, and denial of service attacks.</p> <blockquote> <p>[!WARNING] <a href="https://redirect.github.com/ruby/net-imap/pull/665">ruby/net-imap#665</a> fixes a <code>STARTTLS</code> stripping vulnerability (GHSA-vcgp-9326-pqcp). Without this fix, a man-in-the-middle attacker can cause <code>Net::IMAP#starttls</code> to return "successfully", <strong><em>without starting TLS</em></strong>.</p> </blockquote> <blockquote> <p>[!IMPORTANT] Argument validation is significantly improved. Several command injection vulnerabilities have been fixed: <a href="https://redirect.github.com/ruby/net-imap/pull/662">ruby/net-imap#662</a> fixes CRLF/command/argument injection via Symbol arguments (GHSA-75xq-5h9v-w6px). <a href="https://redirect.github.com/ruby/net-imap/pull/662">ruby/net-imap#662</a> fixes CRLF/command/argument injection via the <code>attr</code> argument to <code>#store</code>/<code>#uid_store</code> (GHSA-hm49-wcqc-g2xg)</p> </blockquote> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/ruby/net-imap/commit/ce20fc8e9974ed87d0848dc411eef490623cdeb9"><code>ce20fc8</code></a> 🔖 Bump version to 0.5.15</li> <li><a href="https://github.com/ruby/net-imap/commit/0b7b83c19312dd3f44cb72be2e7681289324d9db"><code>0b7b83c</code></a> 🔀 Merge pull request <a href="https://redirect.github.com/ruby/net-imap/issues/703">#703</a> from ruby/backport/v0.5/security-patches</li> <li><a href="https://github.com/ruby/net-imap/commit/f22fd6cee01140b53cdb7fc8d42a352a734f4509"><code>f22fd6c</code></a> 🍒 pick 0ea9eba3 (<a href="https://redirect.github.com/ruby/net-imap/issues/701">#701</a>): ✅ Fix flaky tests for MacOS, TruffleRuby</li> <li><a href="https://github.com/ruby/net-imap/commit/12460740451d064cef84f59dd8c91baba0388ae1"><code>1246074</code></a> 🍒 pick ae9f83b5 (<a href="https://redirect.github.com/ruby/net-imap/issues/701">#701</a>): ♻️ Extract str.bytesize lvar in send_literal</li> <li><a href="https://github.com/ruby/net-imap/commit/a2f61af6b7a7e6f0e4f27691531de6e4861318cd"><code>a2f61af</code></a> 🍒 pick 62a0da6d (<a href="https://redirect.github.com/ruby/net-imap/issues/701">#701</a>): 🥅 Validate non-synchronizing literals support</li> <li><a href="https://github.com/ruby/net-imap/commit/e33348ccdc639d0d4414f72e4ba7024bdb24bf29"><code>e33348c</code></a> 🍒 pick d6ddd294 (<a href="https://redirect.github.com/ruby/net-imap/issues/700">#700</a>): 🐛 Prevent trailing <code>{0}</code> in RawData validation</li> <li><a href="https://github.com/ruby/net-imap/commit/4f81b69044297de02ada859fffba6ad9923a43a5"><code>4f81b69</code></a> 🍒 pick 1f97168b (<a href="https://redirect.github.com/ruby/net-imap/issues/699">#699</a>): 🥅 Validate <code>#enable</code> arguments are all atoms</li> <li><a href="https://github.com/ruby/net-imap/commit/69da4a490dd148526fb287a5c82056bfd20c6f95"><code>69da4a4</code></a> 🍒 pick 8d9397ab (<a href="https://redirect.github.com/ruby/net-imap/issues/698">#698</a>): 🥅 Validate QuotedString contains only valid bytes</li> <li><a href="https://github.com/ruby/net-imap/commit/7aab580ad35f2cad1404a58bf0ff015567f2593c"><code>7aab580</code></a> 🍒 pick e3c50fad (<a href="https://redirect.github.com/ruby/net-imap/issues/698">#698</a>): ♻️ Refactor RawText, add improve test coverage</li> <li><a href="https://github.com/ruby/net-imap/commit/fac1733d45ec57da3675c4d266c771c73c7265bb"><code>fac1733</code></a> 🍒 pick aab64f92 (<a href="https://redirect.github.com/ruby/net-imap/issues/686">#686</a>): 🧵 Fix deadlock in <code>#disconnect</code></li> <li>Additional commits viewable in <a href="https://github.com/ruby/net-imap/compare/v0.4.24...v0.5.15">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/chatwoot/chatwoot/network/alerts). </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sony Mathew <sony@chatwoot.com> Co-authored-by: Sony Mathew <2040199+sony-mathew@users.noreply.github.com> |
||
|
|
b1c2db5435 |
fix: Stabilize help center builder error spec (#14725)
## Description Stabilizes the enterprise help center article builder source URL validation spec by asserting the custom exception via its class name and message text. This keeps the spec focused on the intended behavior while avoiding brittle custom exception constant identity checks in CI/reloading environments. A bunch of builds on different PRs have been failing because of this error, sample traces are below: * https://app.circleci.com/pipelines/github/chatwoot/chatwoot/114064/workflows/bdb6eca9-3b65-4c38-b8cf-f2f8564476f8/jobs/158777 * https://app.circleci.com/pipelines/github/chatwoot/chatwoot/114064/workflows/bdb6eca9-3b65-4c38-b8cf-f2f8564476f8/jobs/158777 Fixes # N/A ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? - `/Users/sonymathew/.rbenv/shims/bundle exec rspec spec/enterprise/services/onboarding/help_center_article_builder_spec.rb` - `/Users/sonymathew/.rbenv/shims/bundle exec rubocop spec/enterprise/services/onboarding/help_center_article_builder_spec.rb` - `git diff --check` ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] My changes generate no new warnings - [x] New and existing unit tests pass locally with my changes - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] Any dependent changes have been merged and published in downstream modules |
||
|
|
92a1fb8ab7 |
feat: manage active user sessions from profile (CW-7169) (#14556)
## Description First PR of user_sessions feature - enforcement, impersonation and mfa will be handled separately. Adds an Active Sessions section under Profile where users can see every device currently logged in and revoke any session they don't recognize. Helps users lock down stale or unrecognized logins on their own without needing support. **Behavior at the limit, by client:** - **Browser:** returns 409 with a picker overlay; user picks a session to revoke or chooses "End all sessions" to clear them. - **Mobile / API client:** silently evicts the oldest session and proceeds with login (no picker UI to render). - **Pre-tracking users** (token rows without `user_sessions`, i.e. anyone already logged in before this ships): silent-evict any untracked token first, so freshly tracked sessions are never killed in favor of legacy ones. Sessions are stored in a new `user_sessions` table keyed on `(user_id, client_id)` with browser, platform, IP, last activity and (when configured) geo. Kept in sync with `user.tokens` via an after_save callback so revoking a token from any path cleans up the row. Fixes https://linear.app/chatwoot/issue/CW-7169 ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? - Added specs. - Manual local testing: browser picker fires at limit; pre-tracking user silent-evicts; mixed tracked/untracked correctly drops the untracked one first; profile page revoke succeeds; current session cannot be revoked from profile. ## Checklist - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules |
||
|
|
e055cead35 |
fix: only subscribe calls webhook field when voice calling enabled (#14718)
## Description Solves issue https://github.com/chatwoot/chatwoot/issues/14690 ## Type of change - [ ] Bug fix (non-breaking change which fixes an issue) ## How has this been tested? - UI flows ## Checklist: - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> |
||
|
|
c041fde3a2 |
fix: Preserve original filenames for WhatsApp cloud attachments (#14168)
## Summary Preserves the original filename provided in the WhatsApp Cloud attachment payload when downloading media files. ## Problem Files containing accented or special characters could be saved with malformed names or incorrect extensions due to relying on remote download metadata. ## Changes Made - Uses attachment_payload[:filename] when present - Creates a tempfile using the original filename and extension - Preserves content type metadata - Falls back to existing behavior when no filename is provided ## Notes This should improve attachment downloads for filenames containing accented characters. Related to #10973 --------- Co-authored-by: Rorrick Smith <rorrick@bulksms.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> |
||
|
|
c6a38e2fc6 |
fix: Keep Instagram scopes out of new Messenger OAuth flows (#14695)
Removes Instagram permissions from the Facebook Messenger inbox setup flow to avoid blocking Meta App Review for Messenger-only apps. Meta rejects or stalls Messenger-only app reviews when the OAuth flow requests unrelated Instagram permissions such as `instagram_basic` and `instagram_manage_messages`. This blocks approval for Messenger permissions like `human_agent`, even when the user only wants to configure a Facebook Messenger inbox. New Facebook Messenger inbox setup now requests only the Page and Messenger permissions required for Messenger. Existing Facebook Messenger inboxes that already have Instagram details continue to request Instagram permissions during reauthorization, preserving support for the legacy combined Facebook/Instagram inbox flow. Going forward, new Instagram setups should use the dedicated Instagram channel inbox instead of being bundled into the Messenger setup flow. Closes #13860 ## How to test 1. Go to Settings → Inboxes → Add Inbox → Facebook Messenger. 2. Click Login with Facebook. 3. Verify the OAuth scope does not include `instagram_basic` or `instagram_manage_messages`. 4. Reauthorize an existing Facebook Messenger inbox with `instagram_id` present. 5. Verify the reauth scope still includes `instagram_basic` and `instagram_manage_messages`. --------- Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com> |
||
|
|
2663a8495d |
refactor(security): rename path_without_extensions in Rack::Attack (#14216)
Fix misspelling 'extentions' and clarify comments. Behavior unchanged: same path-stripping logic for throttle matching (e.g. /auth and /auth.json). Also correct 'You may' in remote_ip comment. |
||
|
|
d0d275d962 | fix: cannot resize pasted images (#14709) | ||
|
|
95d6aecb51 |
chore: Add security flags to session cookie configuration (#14248)
## Summary - Adds `httponly` and `secure` flags to session cookie configuration - Adds RSpec tests for session configuration ## Changes - `config/initializers/session_store.rb`: add `httponly: true`, `secure: FORCE_SSL` - `spec/config/session_store_spec.rb`: tests for all session store options Ref #2683 (hardens the session cookie but does not remove it, sessions are still needed for super_admin dashboard) --------- Co-authored-by: Adam-Relay <adam@userelay.ai> Co-authored-by: Adam Berger <adam@adam-berger.com> Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com> |
||
|
|
a901c87ab4 |
perf: merge 3 conversation COUNT queries in /meta into single query (#13536)
# Pull Request Template ## Description - Merges 3 separate COUNT queries in `ConversationFinder#set_count_for_all_conversations` into a single query using PostgreSQL `COUNT(*) FILTER (WHERE ...)` - Every call to `/meta` and conversation index fires 3 COUNT queries against the conversations table — one for "mine", one for "unassigned", one for "all". These share the same base query and scan the same rows, but execute as 3 independent round-trips. from pg_stats | Query | Calls | Total DB Time | Avg Latency | |-------|-------|--------------|-------------| | COUNT unassigned | 4.1M | 141,081 sec | 34ms | | COUNT all | 4.1M | 132,302 sec | 32ms | | COUNT mine | 3.7M | 37,637 sec | 10ms | | **Total** | **~12M** | **311,020 sec** | | `/meta` alone runs at 2.3K RPM (NewRelic), triggered on every WebSocket event (conversation created/updated/status changed/assignee changed). Fixes https://linear.app/chatwoot/issue/INF-43/make-single-query-to-count-the-assigned-unassigned-and-all-instead-of ## Type of change - [x] Performance fix ## How Has This Been Tested? - [x] Verify conversation list shows correct mine/unassigned/assigned/all counts - [x] Verify /meta endpoint returns correct counts - [x] Verify counts update correctly when assigning/unassigning conversations - [x] Verify counts with team filter applied - [x] Monitor DB query count reduction in NewRelic after deploy ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Sony Mathew <ynos1234@gmail.com> |