mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(codebase audit): preserve cleanup without regressions
Fix codebase audit regressions while preserving UI/UX behavior and adding review-driven hardening.
This commit is contained in:
@@ -220,7 +220,7 @@ describe('ChallengeModal', () => {
|
||||
type: 'text/plain',
|
||||
},
|
||||
{
|
||||
challenge: 'base64-image',
|
||||
challenge: 'YmFzZTY0LWltYWdl',
|
||||
type: 'image/png',
|
||||
},
|
||||
],
|
||||
@@ -238,7 +238,7 @@ describe('ChallengeModal', () => {
|
||||
});
|
||||
|
||||
expect(container.textContent).toContain('2/2');
|
||||
expect(container.querySelector('img')?.getAttribute('src')).toBe('data:image/png;base64,base64-image');
|
||||
expect(container.querySelector('img')?.getAttribute('src')).toBe('data:image/png;base64,YmFzZTY0LWltYWdl');
|
||||
|
||||
await clickButton('previous');
|
||||
expect(container.textContent).toContain('1/2');
|
||||
@@ -271,6 +271,7 @@ describe('ChallengeModal', () => {
|
||||
const iframe = container.querySelector('iframe');
|
||||
expect(iframe).not.toBeNull();
|
||||
expect(iframe?.getAttribute('src')).toContain('https://mintpass.org/auth?user=0xabc123&theme=dark');
|
||||
expect(iframe?.getAttribute('sandbox')).toBe('allow-scripts allow-forms allow-popups allow-same-origin allow-top-navigation-by-user-activation');
|
||||
|
||||
await act(async () => {
|
||||
iframe?.dispatchEvent(new Event('load', { bubbles: true }));
|
||||
@@ -438,7 +439,7 @@ describe('ChallengeModal', () => {
|
||||
await renderModal();
|
||||
await clickButton('Open');
|
||||
|
||||
expect(alertSpy).toHaveBeenCalledWith('Error: Invalid URL for authentication challenge');
|
||||
expect(alertSpy).toHaveBeenCalledWith('Error: Only HTTPS iframe challenges or localhost HTTP challenges are supported');
|
||||
expect(testState.abandonCurrentChallengeMock).toHaveBeenCalledOnce();
|
||||
|
||||
await act(async () => {
|
||||
|
||||
@@ -24,7 +24,15 @@ interface ChallengeProps {
|
||||
|
||||
const TextChallenge = ({ challenge }: { challenge: string }) => <div className={styles.challengeMedia}>{challenge}</div>;
|
||||
|
||||
const ImageChallenge = ({ challenge }: { challenge: string }) => <img alt='' className={styles.challengeMedia} src={`data:image/png;base64,${challenge}`} />;
|
||||
const MAX_IMAGE_CHALLENGE_BASE64_LENGTH = 2_000_000;
|
||||
const isSafeBase64ImageChallenge = (challenge: string) => /^[A-Za-z0-9+/]*={0,2}$/.test(challenge) && challenge.length <= MAX_IMAGE_CHALLENGE_BASE64_LENGTH;
|
||||
|
||||
const ImageChallenge = ({ challenge }: { challenge: string }) =>
|
||||
isSafeBase64ImageChallenge(challenge) ? (
|
||||
<img alt='Challenge' className={styles.challengeMedia} src={`data:image/png;base64,${challenge}`} />
|
||||
) : (
|
||||
<div className={styles.challengeMedia}>Invalid image challenge</div>
|
||||
);
|
||||
|
||||
const isLocalIframeHostname = (hostname: string) => hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]' || hostname.endsWith('.localhost');
|
||||
|
||||
@@ -98,7 +106,9 @@ const IframeChallenge = ({
|
||||
const isHttps = validatedUrl.protocol === 'https:';
|
||||
const isLocalHttp = validatedUrl.protocol === 'http:' && isLocalIframeHostname(validatedUrl.hostname);
|
||||
if (!isHttps && !isLocalHttp) {
|
||||
throw new Error('Only HTTPS iframe challenges or localhost HTTP challenges are supported');
|
||||
alert('Error: Only HTTPS iframe challenges or localhost HTTP challenges are supported');
|
||||
onCancel();
|
||||
return;
|
||||
}
|
||||
validatedUrl.pathname = validatedUrl.pathname.replace(/\/{2,}/g, '/');
|
||||
validatedUrl.searchParams.set('theme', theme);
|
||||
@@ -358,15 +368,18 @@ const Challenge = ({ challenge, closeModal, abandonModal }: ChallengeProps) => {
|
||||
<animated.div
|
||||
className={containerClasses.join(' ')}
|
||||
ref={nodeRef}
|
||||
role='dialog'
|
||||
aria-modal='true'
|
||||
aria-labelledby='challenge-modal-title'
|
||||
style={{
|
||||
x: isMobile ? mobileX : x.to((value) => Math.round(value)),
|
||||
y: isMobile ? mobileY : y.to((value) => Math.round(value)),
|
||||
touchAction: 'none',
|
||||
}}
|
||||
>
|
||||
<div className={`challengeHandle ${styles.title}`} {...(!isMobile ? bind() : {})}>
|
||||
<div id='challenge-modal-title' className={`challengeHandle ${styles.title}`} {...(!isMobile ? bind() : {})}>
|
||||
Challenge for {publicationType}
|
||||
<button className={styles.closeIcon} onClick={abandonModal} title='close' />
|
||||
<button type='button' className={styles.closeIcon} onClick={abandonModal} title='close' aria-label={t('close')} />
|
||||
</div>
|
||||
<div className={styles.publication}>
|
||||
{isIframeChallenge ? (
|
||||
|
||||
Reference in New Issue
Block a user