Security hardening: authentication, input validation, OWASP compliance, architecture improvements, and CSP fixes for browser mode (#942)
This commit is contained in:
@@ -6,7 +6,23 @@ import { bindActionCreators } from 'redux'
|
||||
import { connect } from 'react-redux'
|
||||
import { globalActions } from '../../actions'
|
||||
|
||||
const copy = require('copy-text-to-clipboard')
|
||||
// Fallback for older browsers or when clipboard API is not available
|
||||
const copyTextFallback = require('copy-text-to-clipboard')
|
||||
|
||||
async function copyToClipboard(text: string): Promise<boolean> {
|
||||
try {
|
||||
// Try modern Clipboard API first (works in browser with HTTPS)
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
await navigator.clipboard.writeText(text)
|
||||
return true
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Clipboard API failed, using fallback:', error)
|
||||
}
|
||||
|
||||
// Fallback to copy-text-to-clipboard library
|
||||
return copyTextFallback(text)
|
||||
}
|
||||
|
||||
interface Props {
|
||||
value?: string
|
||||
@@ -26,15 +42,24 @@ class Copy extends React.PureComponent<Props, State> {
|
||||
this.state = { didCopy: false }
|
||||
}
|
||||
|
||||
private handleClick = (event: React.MouseEvent) => {
|
||||
private handleClick = async (event: React.MouseEvent) => {
|
||||
event.stopPropagation()
|
||||
|
||||
copy(this.props.value ?? this.props.getValue?.())
|
||||
this.props.actions.global.showNotification('Copied to clipboard')
|
||||
this.setState({ didCopy: true })
|
||||
setTimeout(() => {
|
||||
this.setState({ didCopy: false })
|
||||
}, 1500)
|
||||
const text = this.props.value ?? this.props.getValue?.()
|
||||
if (!text) {
|
||||
return
|
||||
}
|
||||
|
||||
const success = await copyToClipboard(text)
|
||||
if (success) {
|
||||
this.props.actions.global.showNotification('Copied to clipboard')
|
||||
this.setState({ didCopy: true })
|
||||
setTimeout(() => {
|
||||
this.setState({ didCopy: false })
|
||||
}, 1500)
|
||||
} else {
|
||||
this.props.actions.global.showNotification('Failed to copy to clipboard')
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
@@ -45,7 +70,7 @@ class Copy extends React.PureComponent<Props, State> {
|
||||
)
|
||||
|
||||
return (
|
||||
<CustomIconButton onClick={this.handleClick} tooltip="Copy to clipboard">
|
||||
<CustomIconButton onClick={this.handleClick} tooltip="Copy to clipboard" data-testid="copy-button">
|
||||
<div style={{ marginTop: '2px' }}>{icon}</div>
|
||||
</CustomIconButton>
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ interface Props {
|
||||
classes: any
|
||||
style?: React.CSSProperties
|
||||
children?: React.ReactNode
|
||||
'data-testid'?: string
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) => ({
|
||||
@@ -38,7 +39,12 @@ class CustomIconButton extends React.PureComponent<Props, {}> {
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<IconButton className={this.props.classes.button} style={this.props.style} onClick={this.onClick}>
|
||||
<IconButton
|
||||
className={this.props.classes.button}
|
||||
style={this.props.style}
|
||||
onClick={this.onClick}
|
||||
data-testid={this.props['data-testid']}
|
||||
>
|
||||
<Tooltip title={this.props.tooltip} classes={{ popper: this.props.classes.tooltip }}>
|
||||
<span className={this.props.classes.label}>{this.props.children}</span>
|
||||
</Tooltip>
|
||||
|
||||
Reference in New Issue
Block a user