20455: Use noopener everywhere on links and window.open 20455-noopener
authorPeter Amstutz <peter.amstutz@curii.com>
Mon, 4 Mar 2024 16:32:45 +0000 (11:32 -0500)
committerPeter Amstutz <peter.amstutz@curii.com>
Mon, 4 Mar 2024 16:32:45 +0000 (11:32 -0500)
I removed "noreferrer" as this does something different (it prevents
passing the "Referer" header when opening the new URL).  It's not
clear users benefits from suppressing the information that they
navigated to a link from workbench.

Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz@curii.com>

15 files changed:
services/workbench2/src/components/code-snippet/code-snippet.tsx
services/workbench2/src/components/collection-panel-files/collection-panel-files.tsx
services/workbench2/src/components/details-attribute/details-attribute.tsx
services/workbench2/src/store/open-in-new-tab/open-in-new-tab.actions.ts
services/workbench2/src/views-components/context-menu/actions/file-viewer-action.tsx
services/workbench2/src/views-components/context-menu/actions/file-viewer-actions.tsx
services/workbench2/src/views-components/main-app-bar/help-menu.tsx
services/workbench2/src/views-components/repositories-sample-git-dialog/repositories-sample-git-dialog.tsx
services/workbench2/src/views-components/sharing-dialog/sharing-urls-component.tsx
services/workbench2/src/views-components/token-dialog/token-dialog.tsx
services/workbench2/src/views-components/webdav-s3-dialog/webdav-s3-dialog.tsx
services/workbench2/src/views/process-panel/process-io-card.tsx
services/workbench2/src/views/process-panel/process-log-code-snippet.tsx
services/workbench2/src/views/ssh-key-panel/ssh-key-panel-root.tsx
services/workbench2/src/views/virtual-machine-panel/virtual-machine-user-panel.tsx

index 209dbc44b5d37d6221cc6a507fee1401f406dab9..47d8fe1bf029bc5915a069c4a2c12dd08c8051b4 100644 (file)
@@ -54,8 +54,8 @@ const mapStateToProps = (state: RootState): CodeSnippetAuthProps => ({
 export const CodeSnippet = withStyles(styles)(connect(mapStateToProps)(
     ({ classes, lines, linked, className, apiResponse, dispatch, auth, children, inline }: CodeSnippetProps & CodeSnippetAuthProps & DispatchProp) =>
         <Typography
-        component="div"
-        className={classNames([classes.root, className, inline ? classes.inlineRoot: undefined])}>
+            component="div"
+            className={classNames([classes.root, className, inline ? classes.inlineRoot : undefined])}>
             <Typography className={apiResponse ? classes.space : classNames([className, inline ? classes.inline : undefined])} component="pre">
                 {children}
                 {linked ?
@@ -64,7 +64,7 @@ export const CodeSnippet = withStyles(styles)(connect(mapStateToProps)(
                 }
             </Typography>
         </Typography>
-    ));
+));
 
 const renderLinks = (auth: FederationConfig, dispatch: Dispatch) => (text: string): JSX.Element => {
     // Matches UUIDs & PDHs
@@ -78,18 +78,18 @@ const renderLinks = (auth: FederationConfig, dispatch: Dispatch) => (text: strin
             <React.Fragment key={index}>
                 {part}
                 {links[index] &&
-                <Link onClick={() => {
-                    const url = getNavUrl(links[index], auth)
-                    if (url) {
-                        window.open(`${window.location.origin}${url}`, '_blank');
-                    } else {
-                        dispatch(navigationNotAvailable(links[index]));
-                    }
-                }}
-                    style={ {cursor: 'pointer'} }>
-                    {links[index]}
-                </Link>}
+                    <Link onClick={() => {
+                        const url = getNavUrl(links[index], auth)
+                        if (url) {
+                            window.open(`${window.location.origin}${url}`, '_blank', "noopener");
+                        } else {
+                            dispatch(navigationNotAvailable(links[index]));
+                        }
+                    }}
+                        style={{ cursor: 'pointer' }}>
+                        {links[index]}
+                    </Link>}
             </React.Fragment>
         )}
     </>;
-  };
+};
index f1e50e0f0bf7d2f9c4699265e19d515954a9d7cc..e58eb8919814c680bf7732c5ef6ee1886008c674 100644 (file)
@@ -433,7 +433,7 @@ export const CollectionPanelFiles = withStyles(styles)(
                             getInlineFileUrl(enhancedItem.url, config.keepWebServiceUrl, config.keepWebInlineServiceUrl),
                             true
                         );
-                        window.open(fileUrl, "_blank");
+                        window.open(fileUrl, "_blank", "noopener");
                     }
                 }
 
index d965b60f5b591b6f29fb98534643943001be1179..5130db56d1f96314b1e9be12d17cb7aa7c883c55 100644 (file)
@@ -96,10 +96,10 @@ export const DetailsAttribute = connect(mapStateToProps)(withStyles(styles)(
                 if (linkUrl[0] === '/') {
                     valueNode = <Link to={linkUrl} className={classes.link}>{uuid}</Link>;
                 } else {
-                    valueNode = <a href={linkUrl} className={classes.link} target='_blank' rel="noopener noreferrer">{uuid}</a>;
+                    valueNode = <a href={linkUrl} className={classes.link} target='_blank' rel="noopener">{uuid}</a>;
                 }
             } else if (link) {
-                valueNode = <a href={link} className={classes.link} target='_blank' rel="noopener noreferrer">{value}</a>;
+                valueNode = <a href={link} className={classes.link} target='_blank' rel="noopener">{value}</a>;
             } else {
                 valueNode = value;
             }
index 83055e32fcbd3750e50b648c4166f6d618b469de..28da3cf95ab968c62e6d6dd06f956f31e231b97f 100644 (file)
@@ -12,9 +12,9 @@ export const openInNewTabAction = (resource: any) => (dispatch: Dispatch, getSta
     const url = getNavUrl(resource.uuid, getState().auth);
 
     if (url[0] === "/") {
-        window.open(`${window.location.origin}${url}`, "_blank");
+        window.open(`${window.location.origin}${url}`, "_blank", "noopener");
     } else if (url.length) {
-        window.open(url, "_blank");
+        window.open(url, "_blank", "noopener");
     }
 };
 
index 5d98ab6e131bf3ad7716f4a49e5d6a30467265cf..0a77876bac7902c8865c2b364231d04e65b16b9e 100644 (file)
@@ -12,7 +12,7 @@ export const FileViewerAction = (props: any) => {
             style={{ textDecoration: 'none' }}
             href={props.href}
             target="_blank"
-            rel="noopener noreferrer"
+            rel="noopener"
             onClick={props.onClick}>
             <ListItem button>
                 <ListItemIcon>
@@ -20,7 +20,7 @@ export const FileViewerAction = (props: any) => {
                 </ListItemIcon>
                 <ListItemText>
                     Open in new tab
-                    </ListItemText>
+                </ListItemText>
             </ListItem>
         </a>
         : null;
index 6d713d5559c853e34ee19f92ec8d5d3ba9879cff..6eebda24693ef64a66a4b2ef1e3615cc2d7711d2 100644 (file)
@@ -63,6 +63,7 @@ export const FileViewerActions = connect(mapStateToProps)(
                     style={{ textDecoration: 'none' }}
                     href={fillViewerUrl(fileUrl, viewer)}
                     onClick={onClick}
+                    rel="noopener"
                     target='_blank'>
                     <ListItemIcon>
                         {
index af76e4f127daa760b849eb884b26545c5844adf6..1ce2fa1f0f560fc5518fda8d438b96977b8f526d 100644 (file)
@@ -72,7 +72,7 @@ export const HelpMenu = compose(
                 {
                     links.map(link =>
                         <MenuItem key={link.title}>
-                            <a href={link.link} target="_blank" rel="noopener noreferrer" className={classes.link}>
+                            <a href={link.link} target="_blank" rel="noopener" className={classes.link}>
                                 <ImportContactsIcon className={classes.icon} />
                                 <Typography className={classes.linkTitle}>{link.title}</Typography>
                             </a>
index 854f3f4f68173a710317ee23f772f07b8b117ab5..7df99300f7b3312f9f99a09000d42b7d0ba2f9f4 100644 (file)
@@ -54,8 +54,8 @@ export const RepositoriesSampleGitDialog = compose(
                         lines={[snippetText(props.data.uuidPrefix)]} />
                     <Typography variant='body1' className={props.classes.spacing}>
                         See also:
-                        <div><a href="https://doc.arvados.org/user/getting_started/ssh-access-unix.html" className={props.classes.link} target="_blank" rel="noopener noreferrer">SSH access</a></div>
-                        <div><a href="https://doc.arvados.org/user/tutorials/tutorial-firstscript.html" className={props.classes.link} target="_blank" rel="noopener noreferrer">Writing a Crunch Script</a></div>
+                        <div><a href="https://doc.arvados.org/user/getting_started/ssh-access-unix.html" className={props.classes.link} target="_blank" rel="noopener">SSH access</a></div>
+                        <div><a href="https://doc.arvados.org/user/tutorials/tutorial-firstscript.html" className={props.classes.link} target="_blank" rel="noopener">Writing a Crunch Script</a></div>
                     </Typography>
                 </DialogContent>
                 <DialogActions>
@@ -64,7 +64,7 @@ export const RepositoriesSampleGitDialog = compose(
                         color='primary'
                         onClick={props.closeDialog}>
                         Close
-                </Button>
+                    </Button>
                 </DialogActions>
             </Dialog>
     );
index 5facb2e3812e61b43394cb220c299741e64c6451..c17fadd5f09a88c16f183642fbc656752162454f 100644 (file)
@@ -72,7 +72,7 @@ export const SharingURLsComponent = withStyles(styles)((props: SharingURLsCompon
 
                 return <Grid container alignItems='center' key={token.uuid} className={props.classes.sharingUrlRow}>
                     <Grid item>
-                        <Link className={props.classes.sharingUrlText} href={url} target='_blank'>
+                        <Link className={props.classes.sharingUrlText} href={url} target='_blank' rel="noopener">
                             {urlLabel}
                         </Link>
                     </Grid>
index b9d195f58709db99cdd49a7bac6cc6380c2a64b0..e6f3ed582ccd537ef40ba6ed74f548ae3312dc82 100644 (file)
@@ -110,7 +110,7 @@ unset ARVADOS_API_HOST_INSECURE`
                     The Arvados API token is a secret key that enables the Arvados SDKs to access Arvados with the proper permissions.
                     <Typography component='span'>
                         For more information see
-                        <a href='http://doc.arvados.org/user/reference/api-tokens.html' target='blank' className={classes.link}>
+                        <a href='http://doc.arvados.org/user/reference/api-tokens.html' target='blank' rel="noopener" className={classes.link}>
                             Getting an API token.
                         </a>
                     </Typography>
@@ -119,7 +119,7 @@ unset ARVADOS_API_HOST_INSECURE`
                 <DetailsAttributeComponent label='API Host' value={data.apiHost} copyValue={data.apiHost} onCopy={this.onCopy} />
                 <DetailsAttributeComponent label='API Token' value={data.token} copyValue={data.token} onCopy={this.onCopy} />
                 <DetailsAttributeComponent label='Token expiration' value={tokenExpiration} />
-                { this.props.canCreateNewTokens && <Button
+                {this.props.canCreateNewTokens && <Button
                     onClick={() => this.onGetNewToken()}
                     color="primary"
                     size="small"
@@ -127,7 +127,7 @@ unset ARVADOS_API_HOST_INSECURE`
                     className={classes.actionButton}
                 >
                     GET NEW TOKEN
-                </Button> }
+                </Button>}
 
                 <Typography paragraph={true}>
                     Paste the following lines at a shell prompt to set up the necessary environment for Arvados SDKs to authenticate to your account.
@@ -145,7 +145,7 @@ unset ARVADOS_API_HOST_INSECURE`
                 </CopyToClipboard>
                 <Typography>
                     Arvados
-                            <a href='http://doc.arvados.org/user/reference/api-tokens.html' target='blank' className={classes.link}>virtual machines</a>
+                    <a href='http://doc.arvados.org/user/reference/api-tokens.html' target='blank' rel="noopener" className={classes.link}>virtual machines</a>
                     do this for you automatically. This setup is needed only when you use the API remotely (e.g., from your own workstation).
                 </Typography>
             </DialogContent>
@@ -160,4 +160,3 @@ export const TokenDialog =
     withStyles(styles)(
         connect(getTokenDialogData)(
             withDialog(TOKEN_DIALOG_NAME)(TokenDialogComponent)));
-
index a32044a711ef36a70820596ceb509d5a976665e9..5aab053d8efc1914098a32a822a65c16f27702ba 100644 (file)
@@ -170,7 +170,7 @@ export const WebDavS3InfoDialog = compose(
 
                     <DetailsAttribute
                         label='Internet address'
-                        value={<a href={winDav.toString()} target="_blank" rel="noopener noreferrer">{winDav.toString()}</a>}
+                        value={<a href={winDav.toString()} target="_blank" rel="noopener">{winDav.toString()}</a>}
                         copyValue={winDav.toString()} />
 
                     <DetailsAttribute
@@ -202,7 +202,7 @@ export const WebDavS3InfoDialog = compose(
                 <TabPanel index={0} value={activeTab}>
                     <DetailsAttribute
                         label='Server'
-                        value={<a href={cyberDavStr} target="_blank" rel="noopener noreferrer">{cyberDavStr}</a>}
+                        value={<a href={cyberDavStr} target="_blank" rel="noopener">{cyberDavStr}</a>}
                         copyValue={cyberDavStr} />
 
                     <DetailsAttribute
index da4d150a299fe9d9193fa9b0ffa6f42cf14863f1..5716340edc157342f97fd7534da09757d966faf0 100644 (file)
@@ -854,7 +854,7 @@ const KeepUrlPath = withStyles(styles)(({ auth, res, pdh, classes }: KeepUrlProp
                 className={classes.keepLink}
                 href={keepUrlPathNav}
                 target="_blank"
-                rel="noopener noreferrer"
+                rel="noopener"
             >
                 {keepUrlPath || "/"}
             </a>
@@ -934,6 +934,7 @@ const fileToProcessIOValue = (file: File, secondary: boolean, auth: AuthState, p
                 <MuiLink
                     href={file.location}
                     target="_blank"
+                    rel="noopener"
                 >
                     {file.location}
                 </MuiLink>
index 50d343d6223c31f0c4a5998298d415ccaaa688bd..77857822a4d6df059b2ab5e342f069f6fe194eef 100644 (file)
@@ -33,7 +33,7 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
         },
     },
     logText: {
-        padding: `0 ${theme.spacing.unit*0.5}px`,
+        padding: `0 ${theme.spacing.unit * 0.5}px`,
     },
     wordWrap: {
         whiteSpace: 'pre-wrap',
@@ -73,21 +73,21 @@ const renderLinks = (fontSize: number, auth: FederationConfig, dispatch: Dispatc
     }
     return <Typography style={{ fontSize: fontSize }}>
         {text.split(REGEX).map((part, index) =>
-        <React.Fragment key={index}>
-            {part}
-            {links[index] &&
-            <Link onClick={() => {
-                const url = getNavUrl(links[index], auth)
-                if (url) {
-                    window.open(`${window.location.origin}${url}`, '_blank');
-                } else {
-                    dispatch(navigationNotAvailable(links[index]));
-                }
-            }}
-                style={ {cursor: 'pointer'} }>
-                {links[index]}
-            </Link>}
-        </React.Fragment>
+            <React.Fragment key={index}>
+                {part}
+                {links[index] &&
+                    <Link onClick={() => {
+                        const url = getNavUrl(links[index], auth)
+                        if (url) {
+                            window.open(`${window.location.origin}${url}`, '_blank', "noopener");
+                        } else {
+                            dispatch(navigationNotAvailable(links[index]));
+                        }
+                    }}
+                        style={{ cursor: 'pointer' }}>
+                        {links[index]}
+                    </Link>}
+            </React.Fragment>
         )}
     </Typography>;
 };
@@ -97,7 +97,7 @@ const mapStateToProps = (state: RootState): ProcessLogCodeSnippetAuthProps => ({
 });
 
 export const ProcessLogCodeSnippet = withStyles(styles)(connect(mapStateToProps)(
-    ({classes, lines, fontSize, auth, dispatch, wordWrap}: ProcessLogCodeSnippetProps & WithStyles<CssRules> & ProcessLogCodeSnippetAuthProps & DispatchProp) => {
+    ({ classes, lines, fontSize, auth, dispatch, wordWrap }: ProcessLogCodeSnippetProps & WithStyles<CssRules> & ProcessLogCodeSnippetAuthProps & DispatchProp) => {
         const [followMode, setFollowMode] = useState<boolean>(true);
         const scrollRef = useRef<HTMLDivElement>(null);
 
@@ -112,18 +112,18 @@ export const ProcessLogCodeSnippet = withStyles(styles)(connect(mapStateToProps)
             <div ref={scrollRef} className={classes.root}
                 onScroll={(e) => {
                     const elem = e.target as HTMLDivElement;
-                    if (elem.scrollTop + (elem.clientHeight*1.1) >= elem.scrollHeight) {
+                    if (elem.scrollTop + (elem.clientHeight * 1.1) >= elem.scrollHeight) {
                         setFollowMode(true);
                     } else {
                         setFollowMode(false);
                     }
                 }}>
-                { lines.map((line: string, index: number) =>
-                <Typography key={index} component="pre"
-                    className={classNames(classes.logText, wordWrap ? classes.wordWrap : undefined)}>
-                    {renderLinks(fontSize, auth, dispatch)(line)}
-                </Typography>
-                ) }
+                {lines.map((line: string, index: number) =>
+                    <Typography key={index} component="pre"
+                        className={classNames(classes.logText, wordWrap ? classes.wordWrap : undefined)}>
+                        {renderLinks(fontSize, auth, dispatch)(line)}
+                    </Typography>
+                )}
             </div>
         </MuiThemeProvider>
     }));
index 8a266d00c6f146cc7bb0a5853e236224262f8f8f..344352adc7029eebcf745447dc582cf7fb2a21c1 100644 (file)
@@ -12,8 +12,8 @@ type CssRules = 'root' | 'link' | 'buttonContainer' | 'table' | 'tableRow' | 'ke
 
 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     root: {
-       width: '100%',
-       overflow: 'auto'
+        width: '100%',
+        overflow: 'auto'
     },
     link: {
         color: theme.palette.primary.main,
@@ -55,16 +55,16 @@ export const SshKeyPanelRoot = withStyles(styles)(
             <CardContent>
                 <Grid container direction="row">
                     <Grid item xs={8}>
-                        { !hasKeys && <Typography  paragraph={true} >
+                        {!hasKeys && <Typography paragraph={true} >
                             You have not yet set up an SSH public key for use with Arvados.
                             <a href='https://doc.arvados.org/user/getting_started/ssh-access-unix.html'
-                                target='blank' className={classes.link}>
+                                target='blank' rel="noopener" className={classes.link}>
                                 Learn more.
                             </a>
                         </Typography>}
-                        { !hasKeys && <Typography  paragraph={true}>
+                        {!hasKeys && <Typography paragraph={true}>
                             When you have an SSH key you would like to use, add it using button below.
-                        </Typography> }
+                        </Typography>}
                     </Grid>
                     <Grid item xs={4} className={classes.buttonContainer}>
                         <Button onClick={openSshKeyCreateDialog} color="primary" variant="contained">
@@ -113,4 +113,4 @@ export const SshKeyPanelRoot = withStyles(styles)(
                 </Grid>
             </CardContent>
         </Card>
-    );
+);
index 56c92805e24946a0499821fd31c7afb77dc48dce..36d432f95a98ed7a2222be97a5f9e445c09ac347 100644 (file)
@@ -174,7 +174,7 @@ const CardContentWithVirtualMachines = (props: VirtualMachineProps) =>
                         {virtualMachineSendRequest(props)}
                     </div>
                     <div className={props.classes.icon}>
-                        <a href="https://doc.arvados.org/user/getting_started/vm-login-with-webshell.html" target="_blank" rel="noopener noreferrer" className={props.classes.linkIcon}>
+                        <a href="https://doc.arvados.org/user/getting_started/vm-login-with-webshell.html" target="_blank" rel="noopener" className={props.classes.linkIcon}>
                             <Tooltip title="Access VM using webshell">
                                 <HelpIcon />
                             </Tooltip>
@@ -219,7 +219,7 @@ const virtualMachinesTable = (props: VirtualMachineProps) =>
                         const command = `ssh ${username}@${it.hostname}${props.hostSuffix}`;
                         let tokenParam = "";
                         if (props.tokenLocation === SESSION_STORAGE || props.tokenLocation === EXTRA_TOKEN) {
-                          tokenParam = `&token=${encodeURIComponent(props.token)}`;
+                            tokenParam = `&token=${encodeURIComponent(props.token)}`;
                         }
                         const loginHref = `/webshell/?host=${encodeURIComponent(props.webshellUrl + '/' + it.hostname)}&timeout=${props.idleTimeout}&login=${encodeURIComponent(username)}${tokenParam}`;
                         return <TableRow key={lk.uuid}>
@@ -228,11 +228,11 @@ const virtualMachinesTable = (props: VirtualMachineProps) =>
                             <TableCell>
                                 <Grid container spacing={8} className={props.classes.chipsRoot}>
                                     {
-                                    (lk.properties.groups || []).map((group, i) => (
-                                        <Grid item key={i}>
-                                            <Chip label={group} />
-                                        </Grid>
-                                    ))
+                                        (lk.properties.groups || []).map((group, i) => (
+                                            <Grid item key={i}>
+                                                <Chip label={group} />
+                                            </Grid>
+                                        ))
                                     }
                                 </Grid>
                             </TableCell>
@@ -253,8 +253,8 @@ const virtualMachinesTable = (props: VirtualMachineProps) =>
                                     size="small"
                                     href={loginHref}
                                     target="_blank"
-                                    rel="noopener noreferrer">
-                                        Log in as {username}
+                                    rel="noopener">
+                                    Log in as {username}
                                 </Button>
                             </TableCell>
                         </TableRow>;