Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code ead15a0705 chore: improve monitoring for fix: guard links open action against invalid absol
Added code-level monitoring for prevented invalid link-open attempts, at warning level, instead of letting them surface as uncaught runtime errors.

What changed:
- Added `captureInvalidLinksFieldUrlOpenAttempt` utility that reports a Sentry warning (`captureMessage`) with scoped metadata:
  - `fieldName`
  - `recordId`
  - `urlLength`
  - `hasProtocol`
- The LINKS click handler now invokes this monitor when validation fails.

Why:
- Improves triage for malformed link data patterns without crashing the user flow.
- Uses warning-level signal for data-quality/user-input issues instead of noisy uncaught exception noise.
2026-05-12 09:29:06 +00:00
Sonarly Claude Code 653b369cf2 fix: guard links open action against invalid absolute URLs
https://sonarly.com/issue/36832?type=bug

Clicking the “open link” secondary action on a LINKS field can throw a browser DOMException when the stored link value is malformed, causing an uncaught frontend error in the inquiries object view.

Fix: Implemented a targeted frontend fix in the LINKS secondary-action click path so invalid normalized URLs are no longer passed to `window.open`.

What changed:
- In `useGetSecondaryRecordTableCellButton`, the LINKS action now:
  1) normalizes with `ensureAbsoluteUrl(url)`,
  2) validates with `isValidUrl(absoluteUrl)`,
  3) returns early (no open attempt) when invalid,
  4) only calls `window.open` for valid URLs.

Why this is the right layer:
- The crash originates in this UI action handler (`window.open(...)`), so guarding at this boundary prevents user-impacting exceptions regardless of upstream malformed record values.
- I did not modify shared URL normalization behavior globally, avoiding broad side effects.

Pre-edit safety checks completed:
- Checked recent history and no exact fix exists in the last 30 days for the affected files.
- Reviewed recent commits touching this hook to match local implementation style.
- Ran `git blame` on changed lines; this change preserves prior intent (open/copy behavior) while adding validation safety.
- Verified blast radius of new behavior is limited to this hook’s LINKS action path.
2026-05-12 09:29:05 +00:00
2 changed files with 48 additions and 2 deletions
@@ -9,10 +9,11 @@ import { isFieldEmails } from '@/object-record/record-field/ui/types/guards/isFi
import { isFieldLinks } from '@/object-record/record-field/ui/types/guards/isFieldLinks';
import { isFieldPhones } from '@/object-record/record-field/ui/types/guards/isFieldPhones';
import { useRecordFieldValue } from '@/object-record/record-store/hooks/useRecordFieldValue';
import { captureInvalidLinksFieldUrlOpenAttempt } from '@/object-record/record-table/record-table-cell/utils/captureInvalidLinksFieldUrlOpenAttempt';
import { t } from '@lingui/core/macro';
import { useContext } from 'react';
import { FieldMetadataSettingsOnClickAction } from 'twenty-shared/types';
import { ensureAbsoluteUrl, isDefined } from 'twenty-shared/utils';
import { ensureAbsoluteUrl, isDefined, isValidUrl } from 'twenty-shared/utils';
import { IconArrowUpRight, IconCopy, IconMail } from 'twenty-ui/display';
import { useCopyToClipboard } from '~/hooks/useCopyToClipboard';
@@ -85,7 +86,19 @@ export const useGetSecondaryRecordTableCellButton = () => {
if (isFieldLinks(fieldDefinition)) {
const url = (fieldValue as FieldLinksValue).primaryLinkUrl ?? '';
openLinkOnClick = () => {
window.open(ensureAbsoluteUrl(url), '_blank');
const absoluteUrl = ensureAbsoluteUrl(url);
if (!isValidUrl(absoluteUrl)) {
void captureInvalidLinksFieldUrlOpenAttempt({
fieldName: fieldDefinition.metadata.fieldName,
recordId,
url: absoluteUrl,
});
return;
}
window.open(absoluteUrl, '_blank');
};
copyOnClick = () => {
copyToClipboard(url, t`Link copied to clipboard`);
@@ -0,0 +1,33 @@
type CaptureInvalidLinksFieldUrlOpenAttemptArgs = {
fieldName: string;
recordId: string;
url: string;
};
export const captureInvalidLinksFieldUrlOpenAttempt = async ({
fieldName,
recordId,
url,
}: CaptureInvalidLinksFieldUrlOpenAttemptArgs) => {
try {
const { captureMessage, withScope } = await import('@sentry/react');
withScope((scope) => {
scope.setLevel('warning');
scope.setTag('feature', 'record-table-cell-secondary-action');
scope.setContext('invalidLinksFieldUrlOpenAttempt', {
fieldName,
hasProtocol: /^\w+:/.test(url),
recordId,
urlLength: url.length,
});
captureMessage('Invalid links field URL prevented from opening');
return scope;
});
} catch (error) {
// oxlint-disable-next-line no-console
console.warn('Failed to capture invalid links field URL open attempt:', error);
}
};