From 7dca1de1aa549d00b76125b42f2aa41800101dc0 Mon Sep 17 00:00:00 2001 From: RJ Date: Mon, 24 Nov 2025 15:59:18 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Phase=201=20for=20produ?= =?UTF-8?q?ction=20readyness?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 42 ++++++++++++++++++++++++ .env.example | 6 ++++ app/blog/[...slug]/page.tsx | 2 +- app/layout.tsx | 2 +- components/blog/markdown-renderer.tsx | 13 +++++++- components/layout/breadcrumbs-schema.tsx | 2 +- docker-compose.prod.yml | 2 +- lib/markdown.ts | 9 +++++ lib/remark-copy-images.ts | 5 +-- next-env.d.ts | 2 +- 10 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1256b8c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,42 @@ +# Git +.git +.github +.gitignore + +# Dependencies +node_modules + +# Next.js +.next +out + +# Environment +.env* +!.env.example + +# Logs +*.log +npm-debug.log* +logs/ + +# Documentation +*.md +!README.md +specs/ + +# IDE +.vscode +.idea + +# OS +.DS_Store +Thumbs.db + +# Testing +.coverage +.nyc_output + +# Misc +*.swp +*.swo +*~ diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3bbe861 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +# Production site URL (REQUIRED for SEO) +NEXT_PUBLIC_SITE_URL=https://yourdomain.com + +# Environment +NODE_ENV=production +PORT=3030 diff --git a/app/blog/[...slug]/page.tsx b/app/blog/[...slug]/page.tsx index c97fac1..89ad1be 100644 --- a/app/blog/[...slug]/page.tsx +++ b/app/blog/[...slug]/page.tsx @@ -76,7 +76,7 @@ export default async function BlogPostPage({ params }: { params: Promise<{ slug: const relatedPosts = await getRelatedPosts(slugPath) const headings = extractHeadings(post.content) - const fullUrl = `https://yourdomain.com/blog/${slugPath}` + const fullUrl = `${process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3030'}/blog/${slugPath}` return ( <> diff --git a/app/layout.tsx b/app/layout.tsx index fdef6eb..8875eb8 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -11,7 +11,7 @@ export const metadata: Metadata = { default: 'Terminal Blog - Build. Write. Share.', }, description: 'Explorează idei despre dezvoltare, design și tehnologie', - metadataBase: new URL('http://localhost:3000'), + metadataBase: new URL(process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3030'), authors: [{ name: 'Terminal User' }], keywords: ['blog', 'dezvoltare web', 'nextjs', 'react', 'typescript', 'terminal'], openGraph: { diff --git a/components/blog/markdown-renderer.tsx b/components/blog/markdown-renderer.tsx index 78b3d66..07be1ac 100644 --- a/components/blog/markdown-renderer.tsx +++ b/components/blog/markdown-renderer.tsx @@ -2,6 +2,7 @@ import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' +import rehypeSanitize from 'rehype-sanitize' import rehypeRaw from 'rehype-raw' import { OptimizedImage } from './OptimizedImage' import { CodeBlock } from './code-block' @@ -17,7 +18,17 @@ export default function MarkdownRenderer({ content, className = '' }: MarkdownRe
{ if (!src || typeof src !== 'string') return null diff --git a/components/layout/breadcrumbs-schema.tsx b/components/layout/breadcrumbs-schema.tsx index 6bfd7b2..171a56a 100644 --- a/components/layout/breadcrumbs-schema.tsx +++ b/components/layout/breadcrumbs-schema.tsx @@ -12,7 +12,7 @@ export function BreadcrumbsSchema({ items }: { items: BreadcrumbSchemaItem[] }) '@type': 'ListItem', position: item.position, name: item.name, - item: `http://localhost:3000${item.item}`, + item: `${process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3030'}${item.item}`, })), } diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 6ac4caa..b1c5190 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -59,7 +59,7 @@ services: # Docker monitors the application and marks it unhealthy if checks fail # If container is unhealthy, restart policy will trigger a restart healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3030/", "||", "exit", "1"] + test: ["CMD-SHELL", "curl -f http://localhost:3030/ || exit 1"] interval: 30s # Check every 30 seconds timeout: 10s # Wait up to 10 seconds for response retries: 3 # Mark unhealthy after 3 consecutive failures diff --git a/lib/markdown.ts b/lib/markdown.ts index 86674f8..8424557 100644 --- a/lib/markdown.ts +++ b/lib/markdown.ts @@ -15,6 +15,15 @@ export function sanitizePath(inputPath: string): string { if (normalized.includes('..') || path.isAbsolute(normalized)) { throw new Error('Invalid path') } + + // CRITICAL: Verify resolved path stays within content directory + const resolvedPath = path.resolve(POSTS_PATH, normalized) + const allowedBasePath = path.resolve(POSTS_PATH) + + if (!resolvedPath.startsWith(allowedBasePath)) { + throw new Error('Path traversal attempt detected') + } + return normalized } diff --git a/lib/remark-copy-images.ts b/lib/remark-copy-images.ts index ac701c3..6fc273c 100644 --- a/lib/remark-copy-images.ts +++ b/lib/remark-copy-images.ts @@ -39,8 +39,9 @@ async function copyAndRewritePath(node: ImageNode, options: Options): Promise /// -import "./.next/dev/types/routes.d.ts"; +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.