Created
December 12, 2025 02:35
-
-
Save l-portet/845ab6bd953e36f9996d5fb1d0487bc9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const TIMEOUT_MS = 5000; | |
| const CHECK_INTERVAL_MS = 500; | |
| let startTime = Date.now(); | |
| const parseVersion = (versionStr) => { | |
| if (!versionStr) return null; | |
| const [main, pre] = versionStr.split('-'); | |
| const [major, minor, patch] = main.split('.').map(Number); | |
| let canaryVal = 0; | |
| if (pre && pre.includes('canary')) { | |
| const parts = pre.split('.'); | |
| canaryVal = parts.length > 1 ? Number(parts[1]) : 0; | |
| } | |
| return { | |
| major, minor, patch, | |
| isCanary: !!pre, | |
| canaryVal, | |
| raw: versionStr | |
| }; | |
| }; | |
| const isVulnerable = (v) => { | |
| if (!v) return false; | |
| // Next.js 14: Only Canary 14.3.0-canary.77+ is affected. Stable is safe. | |
| if (v.major === 14) { | |
| if (!v.isCanary) return false; // Stable 14 is safe | |
| if (v.minor < 3) return false; | |
| if (v.minor > 3) return true; | |
| // Minor is 3 | |
| if (v.patch > 0) return true; | |
| // Patch is 0, check canary build | |
| return v.canaryVal >= 77; | |
| } | |
| // Next.js 15: Fixed in specific patch versions | |
| if (v.major === 15) { | |
| const fixes = { 0: 5, 1: 9, 2: 6, 3: 6, 4: 8, 5: 7 }; | |
| // If exact minor is known, check patch level | |
| if (fixes.hasOwnProperty(v.minor)) { | |
| return v.patch < fixes[v.minor]; | |
| } | |
| // If minor is older than checked versions (unlikely for v15), vuln. | |
| // If minor is newer (e.g. 15.6), assume fix carried over (Safe). | |
| return v.minor < 0; | |
| } | |
| // Next.js 16: Fixed in 16.0.7 | |
| if (v.major === 16) { | |
| if (v.minor === 0) return v.patch < 7; | |
| return false; // Assume 16.1+ is safe | |
| } | |
| return false; | |
| }; | |
| const showWarning = (version) => { | |
| const el = document.createElement('div'); | |
| el.innerHTML = ` | |
| <div style="font-weight: 700; margin-bottom: 4px;">⚠️ Security Warning</div> | |
| Next.js v${version} is vulnerable.<br/> | |
| <span style="font-size: 0.85em; opacity: 0.9;">Update to a patched release.</span> | |
| `; | |
| Object.assign(el.style, { | |
| position: 'fixed', top: '20px', right: '20px', | |
| background: '#fff3cd', color: '#856404', border: '1px solid #ffeeba', | |
| padding: '12px 16px', borderRadius: '6px', | |
| fontFamily: 'system-ui, sans-serif', fontSize: '14px', | |
| zIndex: '9999999', boxShadow: '0 4px 12px rgba(0,0,0,0.1)', | |
| pointerEvents: 'none' | |
| }); | |
| document.body.appendChild(el); | |
| console.warn(`[Security] Next.js v${version} is vulnerable.`); | |
| }; | |
| const check = () => { | |
| const versionStr = window?.next?.version; | |
| if (!versionStr) return; | |
| const v = parseVersion(versionStr); | |
| if (isVulnerable(v)) { | |
| showWarning(v.raw); | |
| } else { | |
| console.log(`[Security] Next.js v${v.raw} is safe.`); | |
| } | |
| }; | |
| const interval = setInterval(() => { | |
| // 1. Check if ready | |
| if (window?.next?.version) { | |
| clearInterval(interval); | |
| check(); | |
| return; | |
| } | |
| // 2. Check timeout (5 seconds) | |
| if (Date.now() - startTime > TIMEOUT_MS) { | |
| clearInterval(interval); | |
| console.log('[Security] Timeout: Next.js version not detected within 5s.'); | |
| } | |
| }, CHECK_INTERVAL_MS); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment