Skip to content
Merged
Changes from 3 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
45 changes: 38 additions & 7 deletions static/gsApp/components/superuser/superuserWarning.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Fragment, useEffect} from 'react';
import {Fragment, useEffect, useLayoutEffect, useRef, useState} from 'react';
import {keyframes} from '@emotion/react';
import {useTheme} from '@emotion/react';
import styled from '@emotion/styled';
Expand All @@ -19,7 +19,7 @@ import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFea
const POLICY_URL =
'https://www.notion.so/sentry/Sentry-Rules-for-Handling-Customer-Data-9612532c37e14eeb943a6a584abbac99';

const SUPERUSER_MESSAGE = 'You are in superuser mode';
const SUPERUSER_MESSAGE = 'You are in superuser mode / Hover for more information';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hover instruction leaks into non-marquee alert context

Medium Severity

The updated SUPERUSER_MESSAGE ("…Hover for more information") is shared across two contexts. On line 110, when hasPageFrame is false, this message is injected into the AlertStore alert — which already displays WARNING_MESSAGE inline right next to it. In that path there's no hoverable marquee strip, so telling users to "hover for more information" is misleading — the information is already visible in the alert itself. The old message ("You are in superuser mode") worked correctly in both contexts.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c95b106. Configure here.

const SUPERUSER_SEPARATOR = ' ///// ';
const WARNING_MESSAGE = (
<Fragment>
Expand Down Expand Up @@ -73,6 +73,34 @@ export function SuperuserWarning({organization, className}: Props) {
const hasPageFrame = useHasPageFrameFeature();
const isExcludedOrg = shouldExcludeOrg(organization);

const stripRef = useRef<HTMLDivElement>(null);
const textRef = useRef<HTMLSpanElement>(null);
const [marqueeCount, setMarqueeCount] = useState(8);

useLayoutEffect(() => {
const strip = stripRef.current;
const text = textRef.current;
if (!strip || !text) {
return;
}

const update = () => {
const stripWidth = strip.clientWidth;
if (!stripWidth || !text.offsetWidth) {
return;
}
setMarqueeCount(currentCount => {
const repWidth = text.offsetWidth / currentCount;
Comment thread
sentry[bot] marked this conversation as resolved.
return repWidth ? Math.ceil(stripWidth / repWidth) + 2 : currentCount;
});
Comment on lines +93 to +96
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: Reading text.offsetWidth inside the setMarqueeCount functional updater can cause a race condition during rapid resizes, as the DOM may not reflect the pending state.
Severity: LOW

Suggested Fix

Capture the text.offsetWidth value before calling setMarqueeCount. Pass this measurement into the state updater or use it to calculate the new count outside the updater. This ensures that the DOM measurement corresponds to the state that was rendered when the measurement was taken, avoiding reliance on currentCount inside the updater for this calculation.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: static/gsApp/components/superuser/superuserWarning.tsx#L93-L96

Potential issue: The `setMarqueeCount` functional updater reads `text.offsetWidth`
directly from the DOM. During rapid viewport resizing, multiple `ResizeObserver`
callbacks can queue state updates before React commits any DOM changes. This creates a
race condition where the updater's `currentCount` argument reflects a pending state, but
`text.offsetWidth` reflects the older, committed DOM state. As a result, the calculation
`repWidth = text.offsetWidth / currentCount` is incorrect, leading to a miscalculation
of the required repetitions and causing visible gaps or unnecessary DOM nodes in the
marquee.

};

update();
const observer = new ResizeObserver(update);
observer.observe(strip);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let's refactor to use the lib?

import {useResizeObserver} from '@react-aria/utils';

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

oh yes sure! thanks for the reminder 🙏

return () => observer.disconnect();
}, [hasPageFrame, isExcludedOrg]);

useEffect(() => {
if (!isExcludedOrg) {
AlertStore.addAlert({
Expand Down Expand Up @@ -109,16 +137,18 @@ export function SuperuserWarning({organization, className}: Props) {
>
<Tooltip
isHoverable
position="top-start"
position="bottom-start"
containerDisplayMode="block"
title={
<TooltipContent>
<Content>{WARNING_MESSAGE}</Content>
<ExitSuperuserButton />
</TooltipContent>
}
>
<MarqueeStrip align="baseline" overflow="hidden">
<MarqueeStrip ref={stripRef} align="baseline" overflow="hidden">
<MarqueeText
ref={textRef}
wrap="nowrap"
monospace
bold
Expand All @@ -129,9 +159,7 @@ export function SuperuserWarning({organization, className}: Props) {
} as React.CSSProperties
}
>
{Array.from({length: 8}, () => SUPERUSER_MESSAGE).join(
SUPERUSER_SEPARATOR
)}
{`${SUPERUSER_MESSAGE}${SUPERUSER_SEPARATOR}`.repeat(marqueeCount)}
</MarqueeText>
</MarqueeStrip>
</Tooltip>
Expand Down Expand Up @@ -187,6 +215,8 @@ const Frame = styled(Container)`
/* Ensures it stays on top of all content */
z-index: 9999;
border-width: ${p => p.theme.border.xl};
/* Keep the marquee strip pinned to the top so the tooltip anchors there too */
align-items: flex-start;
`;

const MarqueeStrip = styled(Flex)`
Expand All @@ -197,6 +227,7 @@ const MarqueeStrip = styled(Flex)`
flex-shrink: 0;
/* Re-enable pointer events so the tooltip is hoverable */
pointer-events: auto;
cursor: help;
`;

const MarqueeText = styled(Text)`
Expand Down
Loading