Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions app/views/RoomInfoView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import isEmpty from 'lodash/isEmpty';
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { ScrollView, View } from 'react-native';
import { type Subscription } from 'rxjs';
import UAParser from 'ua-parser-js';
import Bowser from 'bowser';

import * as HeaderButton from '../../containers/Header/components/HeaderButton';
import SafeAreaView from '../../containers/SafeAreaView';
Expand Down Expand Up @@ -131,10 +131,11 @@ const RoomInfoView = (): React.ReactElement => {
const { visitor } = result;
const params: { os?: string; browser?: string } = {};
if (visitor.userAgent) {
const ua = new UAParser();
ua.setUA(visitor.userAgent);
params.os = `${ua.getOS().name} ${ua.getOS().version}`;
params.browser = `${ua.getBrowser().name} ${ua.getBrowser().version}`;
const ua = Bowser.getParser(visitor.userAgent);
params.os = `${ua.getOS().name ?? ua.getOSName()} ${ua.getOS().version ?? ua.getOSVersion()}`;
params.browser = `${ua.getBrowser().name ?? ua.getBrowserName()} ${
ua.getBrowser().version ?? ua.getBrowserVersion()
}`;
Comment on lines +134 to +138
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Bowser v2.13.1 getOS() getBrowser() return undefined properties API documentation

💡 Result:

Short answer: In Bowser 2.x (including v2.13.1) getBrowser()/getOS() are instance methods on a Parser returned by Bowser.getParser(...) (or new Parser(ua)), not top-level static functions. Calling Bowser.getBrowser() / Bowser.getOS() (or calling instance methods before parsing or with an empty UA) will yield undefined. See docs and parser API.

What to do

  • Use the Parser API:
    const parser = Bowser.getParser(navigator.userAgent);
    const browser = parser.getBrowser(); // { name?, version? }
    const os = parser.getOS(); // { name?, version?, versionName? }
    or use parser.getResult() for the full parsed object.
  • Ensure you pass a non-empty UA and that parsing was not skipped (skipParsing=false) — otherwise properties can be undefined.

Sources

  • Bowser docs / Parser API. [1]
  • parser.js reference showing getBrowser/getOS return possibly undefined name/version fields. [2]

Sources:
[1] https://bowser-js.github.io/bowser/
[2] https://bowser-js.github.io/bowser/docs/parser.js.html


🌐 Web query:

Bowser getOSName() getBrowserName() getOSVersion() getBrowserVersion() methods API

💡 Result:

Short answer: These are methods on Bowser's Parser object. Call Bowser.getParser(userAgent) (or Bowser.getParser(window.navigator.userAgent)), then use getBrowserName()/getBrowserVersion() to get the browser name and version, and getOSName()/getOSVersion() (or getOS()) to get OS name/version. Example:

  • const parser = Bowser.getParser(window.navigator.userAgent); [1]
  • parser.getBrowserName(); // returns browser name string (e.g., "Chrome"). [1][2]
  • parser.getBrowserVersion(); // returns browser version string (e.g., "116.0.5845.96"). [2]
  • parser.getOSName(); // returns OS name string (e.g., "Windows" or "macOS"). [2]
  • parser.getOSVersion(); // returns OS version string (e.g., "10.15.7"). [2]
  • parser.getOS(); // returns { name: "...", version: "..." } if you need both. [2]

Notes: methods live on the Parser instance (Bowser.getParser(...)). See Bowser docs and the Parser API for details and examples. [1][2]

Sources:
[1] Bowser docs (overview / usage).
[2] Bowser Parser API (getBrowserName, getBrowserVersion, getOSName, getOSVersion, getOS).


🌐 Web query:

Bowser v2.13.1 parser getOS() undefined properties when name version not available

💡 Result:

Summary: In Bowser v2.13.x the Parser.getOS() can return an empty object ({} ) when no OS parser matches, and Parser.getOSName() converts the possibly-undefined name with String(name).toLowerCase() — which produces the literal string "undefined" instead of an empty string. This explains why you see undefined properties / the string "undefined" when name/version aren't available. (bowser-js.github.io)

Fix/workarounds:

  • Short-term (call-site): read the raw object and null-coalesce: const os = parser.getOS(); const osName = os.name ?? ''; const osVersion = os.version ?? '';
  • Short-term (use API): avoid getOSName() in affected versions; use parser.getOS().name with null coalescing. (bowser-js.github.io)
  • Long-term (library patch): change getOSName implementation to guard before String(), e.g.:
    getOSName(toLowerCase){ const name = this.getOS().name || ''; return toLowerCase ? name.toLowerCase() : name; }
    (Or wrap with String(name || '')). This prevents "undefined". (bowser-js.github.io)

If you want, I can draft a small PR patch or a one-line patch you can apply to v2.13.1.

Citations:


Avoid using Bowser's convenience methods as fallbacks; they have a bug that produces the string "undefined".

The current fallback pattern (ua.getOS().name ?? ua.getOSName()) attempts to handle cases where getOS().name is undefined, but Bowser v2.13.1's convenience methods (getOSName(), getBrowserName(), etc.) have a bug: when the properties are unavailable, they return the literal string "undefined" instead of an empty string. This causes params.os and params.browser to contain invalid values like "undefined 116.0".

Instead, use null coalescing with empty strings:

params.os = `${ua.getOS().name ?? ''} ${ua.getOS().version ?? ''}`.trim();
params.browser = `${ua.getBrowser().name ?? ''} ${ua.getBrowser().version ?? ''}`.trim();

Or apply .trim() to the final string to clean up extra spaces if either property is missing.

🤖 Prompt for AI Agents
In @app/views/RoomInfoView/index.tsx around lines 134 - 138, The code uses
Bowser.getParser and then falls back to convenience methods
(ua.getOSName()/getBrowserName()) which can return the literal string
"undefined"; replace those fallbacks by only using the parsed objects
(ua.getOS().name and ua.getOS().version; ua.getBrowser().name and
ua.getBrowser().version), coalescing each to an empty string and building
params.os and params.browser from those values, then call .trim() on the final
string to avoid stray spaces (update references around Bowser.getParser,
ua.getOS, ua.getBrowser, params.os and params.browser).

}
setRoomUser({ ...visitor, ...params });
setHeader();
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@rocket.chat/mobile-crypto": "RocketChat/rocket.chat-mobile-crypto",
"@rocket.chat/sdk": "RocketChat/Rocket.Chat.js.SDK#mobile",
"@rocket.chat/ui-kit": "0.31.19",
"bowser": "^2.13.1",
"bytebuffer": "5.0.1",
"color2k": "1.2.4",
"dayjs": "^1.11.18",
Expand Down Expand Up @@ -83,7 +84,6 @@
"lodash": "4.17.21",
"mitt": "3.0.1",
"pretty-bytes": "5.6.0",
"prop-types": "15.7.2",
"react": "19.0.0",
"react-hook-form": "7.34.2",
"react-native": "0.79.4",
Expand Down Expand Up @@ -131,7 +131,6 @@
"semver": "7.5.2",
"transliteration": "2.3.5",
"typed-redux-saga": "1.5.0",
"ua-parser-js": "1.0.33",
"uri-js": "4.4.1",
"url-parse": "1.5.10",
"use-debounce": "9.0.4",
Expand Down
21 changes: 6 additions & 15 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6603,6 +6603,11 @@ boolbase@^1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==

bowser@^2.13.1:
version "2.13.1"
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.13.1.tgz#5a4c652de1d002f847dd011819f5fc729f308a7e"
integrity sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==

[email protected]:
version "0.1.0"
resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e"
Expand Down Expand Up @@ -12405,15 +12410,6 @@ prop-types@*, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-t
object-assign "^4.1.1"
react-is "^16.13.1"

[email protected]:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
dependencies:
loose-envify "^1.4.0"
object-assign "^4.1.1"
react-is "^16.8.1"

property-expr@^2.0.4:
version "2.0.6"
resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.6.tgz#f77bc00d5928a6c748414ad12882e83f24aec1e8"
Expand Down Expand Up @@ -12583,7 +12579,7 @@ [email protected]:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==

react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.1:
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
Expand Down Expand Up @@ -14756,11 +14752,6 @@ [email protected], ua-parser-js@^0.7.33:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.2.tgz#e2976c34dbfb30b15d2c300b2a53eac87c57a775"
integrity sha512-00y/AXhx0/SsnI51fTc0rLRmafiGOM4/O+ny10Ps7f+j/b8p/ZY11ytMgznXkOVo4GQ+KwQG5UQLkLGirsACRg==

[email protected]:
version "1.0.33"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.33.tgz#f21f01233e90e7ed0f059ceab46eb190ff17f8f4"
integrity sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==

ultron@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
Expand Down