| name | security-review |
| description | Security checklist and best practices for the personal blog project |
Security Review Checklist
This skill provides a comprehensive security review checklist for the Next.js + Notion blog.
Environment Variables Security
✅ Required Checks
Implementation
export function validateEnv() {
const required = ['NOTION_API_KEY', 'NOTION_DATABASE_ID'];
for (const key of required) {
if (!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
}
}
validateEnv();
❌ Common Mistakes
const API_KEY = 'ntn_abc123...';
'use client';
const key = process.env.NOTION_API_KEY;
export async function GET() {
const key = process.env.NOTION_API_KEY;
}
API Security
Notion API Best Practices
export async function getPosts(): Promise<NotionPost[]> {
try {
const response = await notion.databases.query({
database_id: process.env.NOTION_DATABASE_ID!,
filter: {
property: 'Published',
checkbox: { equals: true },
},
});
return response.results.map(mapNotionToPost);
} catch (error) {
console.error('Notion API error:', error);
return [];
}
}
Rate Limiting
const RATE_LIMIT = 3;
const cache = new Map<string, number[]>();
export function rateLimit(identifier: string): boolean {
const now = Date.now();
const timestamps = cache.get(identifier) || [];
const recent = timestamps.filter(t => now - t < 1000);
if (recent.length >= RATE_LIMIT) {
return false;
}
recent.push(now);
cache.set(identifier, recent);
return true;
}
XSS Prevention
Content Sanitization
export function NotionRenderer({ blocks }: { blocks: NotionBlock[] }) {
return blocks.map(block => {
switch (block.type) {
case 'paragraph':
return <p>{block.paragraph.rich_text[0]?.plain_text}</p>;
case 'code':
return <SyntaxHighlighter>{block.code.rich_text[0]?.plain_text}</SyntaxHighlighter>;
default:
return null;
}
});
}
❌ Dangerous Patterns
<div dangerouslySetInnerHTML={{ __html: userInput }} />
import DOMPurify from 'isomorphic-dompurify';
<div dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(content)
}} />
CSRF Protection
Next.js provides built-in CSRF protection for Server Actions.
'use server';
export async function submitForm(formData: FormData) {
const email = formData.get('email');
}
SQL Injection Prevention
Not applicable for this project (using Notion API), but if adding a database:
const result = await db.query(
'SELECT * FROM posts WHERE slug = $1',
[slug]
);
const result = await db.query(
`SELECT * FROM posts WHERE slug = '${slug}'`
);
Authentication & Authorization
Currently not implemented, but if adding:
Checklist
Content Security Policy (CSP)
const nextConfig = {
async headers() {
return [
{
source: '/:path*',
headers: [
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-eval' 'unsafe-inline'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"font-src 'self' data:",
"connect-src 'self' https://api.notion.com",
].join('; '),
},
{
key: 'X-Frame-Options',
value: 'DENY',
},
{
key: 'X-Content-Type-Options',
value: 'nosniff',
},
{
key: 'Referrer-Policy',
value: 'strict-origin-when-cross-origin',
},
],
},
];
},
};
Dependency Security
Regular Audits
npm audit
npm audit fix
npm outdated
Dependabot Configuration
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
Vercel Deployment Security
Environment Variables
Security Headers
Notion Integration Security
API Key Management
Data Validation
function mapNotionToPost(page: any): NotionPost {
if (!page.properties.Title?.title?.[0]?.plain_text) {
throw new Error('Invalid post: missing title');
}
if (!page.properties.Slug?.rich_text?.[0]?.plain_text) {
throw new Error('Invalid post: missing slug');
}
return {
id: page.id,
title: page.properties.Title.title[0].plain_text,
slug: page.properties.Slug.rich_text[0].plain_text,
};
}
Security Review Workflow
Before Every Deployment
Monthly Security Tasks
Incident Response Plan
If a security issue is discovered:
- Assess Impact - Determine scope and severity
- Contain - Rotate compromised keys immediately
- Investigate - Review logs and access patterns
- Fix - Deploy security patch
- Notify - Inform affected users if necessary
- Document - Record incident and response
Security Resources