fix: enforce strict full screen while screen sharing

This commit is contained in:
kushal.saha 2026-08-12 12:48:56 +00:00
parent e48f193a64
commit eba3ec96b3

View File

@ -343,6 +343,70 @@ export function uploadTabScreenshotBlob(imageData, reason = 'tab_switch') {
.catch(err => console.log('System screenshot fetch error:', err));
}
export function isEntireScreenShare(track) {
if (!track || !track.getSettings) return true;
const settings = track.getSettings();
const surface = settings.displaySurface;
if (surface) {
const allowedSurfaces = ['monitor', 'screen'];
if (!allowedSurfaces.includes(surface)) {
return false;
}
}
if (window.screen && window.screen.width && settings.width) {
if (settings.width < (window.screen.width * 0.65) && settings.height < (window.screen.height * 0.65)) {
return false;
}
}
return true;
}
export function isScreenSharingActive() {
return Boolean(screenShareStream && screenShareStream.active && screenShareStream.getVideoTracks().some(t => t.readyState === 'live'));
}
export function enforceBrowserFullscreen() {
if (!isScreenSharingActive() || document.fullscreenElement) return;
beginNativePrompt();
if (document.documentElement.requestFullscreen) {
document.documentElement.requestFullscreen()
.then(() => {
endNativePrompt();
armTrailingBuffer(1000);
})
.catch(() => {
endNativePrompt();
showFullscreenRequiredModal();
});
} else {
endNativePrompt();
showFullscreenRequiredModal();
}
}
export function showFullscreenRequiredModal() {
if (!isScreenSharingActive() || document.fullscreenElement) return;
if (typeof window.Swal !== 'undefined') {
window.Swal.fire({
icon: 'warning',
title: 'Fullscreen Mode Required',
text: 'Your assessment must be conducted in Fullscreen mode while screen sharing. Please click below to enter Fullscreen.',
confirmButtonText: 'Enter Fullscreen Mode',
confirmButtonColor: '#6366f1',
allowOutsideClick: false,
allowEscapeKey: false
}).then(() => {
if (isScreenSharingActive()) {
enforceBrowserFullscreen();
}
});
}
}
// --- Screen Share Enforcement & Live Overlay Detector (Category 6) ---
export function startScreenShare() {
if (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia) {
@ -361,26 +425,27 @@ export function startScreenShare() {
.then(stream => {
endNativePrompt();
const track = stream.getVideoTracks()[0];
const settings = track.getSettings ? track.getSettings() : {};
// Enforce ENTIRE SCREEN share
if (settings.displaySurface && settings.displaySurface !== 'monitor') {
// Enforce ENTIRE SCREEN share across all browsers
if (!isEntireScreenShare(track)) {
track.stop();
screenShareStream = null;
logViolation('tab_switch', 'Candidate attempted non-entire screen share (tab or window)');
if (typeof window.Swal !== 'undefined') {
window.Swal.fire({
icon: 'warning',
icon: 'error',
title: 'Entire Screen Share Required',
text: 'You selected a single tab or window. You MUST select "Entire Screen".',
confirmButtonText: 'Reshare Entire Screen',
confirmButtonColor: '#6366f1',
html: 'You selected a single tab or window.<br><br><b>You MUST select "Entire Screen" (or whole monitor)</b> to continue your interview.',
confirmButtonText: 'Reshare Entire Screen Now',
confirmButtonColor: '#ef4444',
allowOutsideClick: false,
allowEscapeKey: false
}).then(() => {
track.stop();
setTimeout(startScreenShare, 300);
});
} else {
alert('Entire Screen share is required. Please select Entire Screen.');
track.stop();
setTimeout(startScreenShare, 300);
}
return;
@ -391,21 +456,9 @@ export function startScreenShare() {
vidElem.srcObject = stream;
vidElem.play().catch(() => {});
const btn = document.getElementById('share-screen-btn');
if (btn) {
btn.innerText = '🟢 Entire Screen Sharing Active';
btn.style.background = '#10b981';
}
updateScreenShareButton(true);
beginNativePrompt();
if (document.documentElement.requestFullscreen) {
document.documentElement.requestFullscreen()
.catch(() => {})
.finally(() => {
endNativePrompt();
armTrailingBuffer(1000);
});
}
enforceBrowserFullscreen();
const interviewId = document.body.dataset.interviewId;
if (typeof window.Peer !== 'undefined' && interviewId) {
@ -417,10 +470,12 @@ export function startScreenShare() {
track.onended = () => {
screenShareStream = null;
if (btn) {
btn.innerText = '🖥️ Share Screen with Interviewer';
btn.style.background = 'linear-gradient(135deg, #6366f1 0%, #0078d4 100%)';
updateScreenShareButton(false);
if (document.fullscreenElement) {
try { document.exitFullscreen().catch(() => {}); } catch(e) {}
}
logViolation('tab_switch', 'Candidate stopped screen sharing');
};
@ -430,14 +485,50 @@ export function startScreenShare() {
endNativePrompt();
console.log('Screen sharing cancelled/denied:', err);
screenShareStream = null;
const btn = document.getElementById('share-screen-btn');
if (btn) {
btn.innerText = '🖥️ Share Screen with Interviewer';
btn.style.background = 'linear-gradient(135deg, #6366f1 0%, #0078d4 100%)';
}
updateScreenShareButton(false);
});
}
export function stopScreenShare() {
if (screenShareStream) {
try {
screenShareStream.getTracks().forEach(track => track.stop());
} catch(e) {}
screenShareStream = null;
}
updateScreenShareButton(false);
if (document.fullscreenElement) {
try { document.exitFullscreen().catch(() => {}); } catch(e) {}
}
logViolation('tab_switch', 'Candidate stopped screen sharing');
}
export function toggleScreenShare() {
if (screenShareStream && screenShareStream.active && screenShareStream.getVideoTracks().length > 0 && screenShareStream.getVideoTracks()[0].readyState === 'live') {
stopScreenShare();
} else {
startScreenShare();
}
}
export function updateScreenShareButton(active) {
const btn = document.getElementById('share-screen-btn');
if (!btn) return;
if (active) {
btn.innerHTML = '🛑 Stop Screen Sharing';
btn.style.background = '#ef4444';
btn.style.borderColor = '#dc2626';
btn.onclick = stopScreenShare;
} else {
btn.innerHTML = '🖥️ Share Screen with Interviewer';
btn.style.background = 'linear-gradient(135deg, #6366f1 0%, #0078d4 100%)';
btn.style.borderColor = '#6366f1';
btn.onclick = startScreenShare;
}
}
export function initLiveOverlayDetector() {
if (overlayCheckInterval) clearInterval(overlayCheckInterval);
@ -624,12 +715,13 @@ export function initCandidateProctoringListeners() {
}
}, 6000);
// 8. Fullscreen Exit Listener
// 8. Fullscreen Exit Listener (Only enforced when screen sharing is active)
document.addEventListener('fullscreenchange', function() {
if (isFocusSuppressed()) return;
if (isFocusSuppressed() || !isScreenSharingActive()) return;
if (!document.fullscreenElement) {
logViolation('tab_switch', 'Candidate exited full-screen proctoring mode (possible overlay AI window interaction)');
logViolation('tab_switch', 'Candidate exited full-screen proctoring mode while screen sharing');
captureAndUploadTabScreenshot();
showFullscreenRequiredModal();
}
});
}
@ -1201,7 +1293,11 @@ if (typeof window !== 'undefined') {
window.endNativePrompt = endNativePrompt;
window.armTrailingBuffer = armTrailingBuffer;
window.isFocusSuppressed = isFocusSuppressed;
window.isScreenSharingActive = isScreenSharingActive;
window.startScreenShare = startScreenShare;
window.stopScreenShare = stopScreenShare;
window.toggleScreenShare = toggleScreenShare;
window.updateScreenShareButton = updateScreenShareButton;
window.initLiveOverlayDetector = initLiveOverlayDetector;
window.initQuestionRepeatDetection = initQuestionRepeatDetection;
window.initCandidateProctoringListeners = initCandidateProctoringListeners;