Claude/audit dependabot setup xfl71#15
Conversation
…al data exposure, safe markdown links only
…ticle titles and summaries
…ession ID generation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eb870d7ca1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; frame-src *; connect-src 'self'; img-src 'self' data: https:"> |
There was a problem hiding this comment.
Allow external scripts required by reader
The new CSP restricts script-src to 'self', but reader.html still loads DOMPurify from https://cdn.jsdelivr.net/... (and gtag from https://www.googletagmanager.com). Browsers enforcing CSP will block those scripts, so when the inline script later calls DOMPurify.sanitize(...) it throws a ReferenceError and breaks the article view for typical URLs that include source/tags. Consider whitelisting the required CDN origins in script-src or self-hosting those scripts so the sanitizer is available.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This pull request implements security improvements and configuration for the AI-Pulse news aggregator, including Dependabot setup, enhanced input sanitization, and Vercel deployment configuration. The changes focus on addressing security vulnerabilities identified by CodeQL analysis.
Key changes include:
- Implementation of cryptographically secure session ID generation using
crypto.getRandomValues() - Enhanced HTML sanitization using DOMPurify library with additional HTML entity escaping
- Addition of Content-Security-Policy headers and URL validation for safer content handling
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| vercel.json | New Vercel deployment configuration with security headers, build settings, and function configuration |
| tracker.js | Improved session ID generation replacing Math.random() with crypto.getRandomValues() for cryptographic security |
| src/aggregator.js | Enhanced sanitization with DOMPurify, added htmlEscape function, removed UTM tracking parameters, and updated README generation links |
| reader.html | Added Content-Security-Policy meta tag and improved URL validation before iframe assignment |
| package.json | Added package metadata (name, version, scripts, engines) and project information |
| .claude/security-guidelines.md | New comprehensive security guidelines document for code contributions |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "functions": { | ||
| "src/aggregator.js": { | ||
| "maxDuration": 300, | ||
| "memory": 1024 |
There was a problem hiding this comment.
The function memory allocation of 1024 MB (1 GB) seems excessive for an RSS aggregator function. Consider starting with a lower value like 512 MB or 256 MB and scaling up only if needed, as this will reduce costs and resource usage.
| "memory": 1024 | |
| "memory": 512 |
| function htmlEscape(input) { | ||
| if (!input) { | ||
| return ''; | ||
| } | ||
| return input | ||
| .replace(/&/g, '&') | ||
| .replace(/</g, '<') | ||
| .replace(/>/g, '>'); |
There was a problem hiding this comment.
The htmlEscape function is incomplete. It only escapes &, <, and > characters but doesn't handle other important HTML entities like quotes (" and '). This could lead to XSS vulnerabilities in attribute contexts. Consider adding escaping for quotes: .replace(/"/g, '"') and .replace(/'/g, ''')
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; frame-src *; connect-src 'self'; img-src 'self' data: https:"> |
There was a problem hiding this comment.
The Content-Security-Policy allows 'unsafe-inline' for both script-src and style-src, which significantly weakens the CSP protection against XSS attacks. Consider using nonces or hashes for inline scripts/styles, or move inline code to external files. Additionally, 'frame-src *' allows iframes from any source which may be too permissive.
| "key": "X-XSS-Protection", | ||
| "value": "1; mode=block" |
There was a problem hiding this comment.
The X-XSS-Protection header is deprecated and should be removed. Modern browsers have removed support for this header, and it can actually introduce security vulnerabilities in older browsers. The Content-Security-Policy header provides better XSS protection.
| "key": "X-XSS-Protection", | |
| "value": "1; mode=block" | |
| "key": "Content-Security-Policy", | |
| "value": "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" |
| "use": "@vercel/static" | ||
| }, | ||
| { | ||
| "src": "*.js", |
There was a problem hiding this comment.
Using "@vercel/static" for "*.js" files will serve all JavaScript files as static assets, which may not be the intended behavior. This could expose server-side JavaScript files that should not be publicly accessible. Consider being more specific about which JS files should be served statically (e.g., only files in a specific public directory).
| "src": "*.js", | |
| "src": "public/*.js", |
src/aggregator.js
Outdated
| // Use DOMPurify to safely remove all HTML/script tags - prevents incomplete sanitization attacks | ||
| const cleanTitle = DOMPurify.sanitize(article.title || '', { ALLOWED_TAGS: [] }); | ||
| const cleanSummary = DOMPurify.sanitize(article.contentSnippet || '', { ALLOWED_TAGS: [] }); | ||
|
|
||
| return { | ||
| title: article.title?.replace(/<[^>]*>/g, '').slice(0, 200) || 'Untitled', | ||
| link: addUTMParams(article.link, category), // UTM tracks traffic FROM AI-Pulse | ||
| title: htmlEscape(cleanTitle).slice(0, 200) || 'Untitled', | ||
| link: article.link, // Direct link, no tracking | ||
| pubDate: new Date(article.pubDate || Date.now()), | ||
| source: sourceName, | ||
| tags: tags, | ||
| category: article.categories?.[0] || 'General', | ||
| summary: smartTruncate(rawSummary, 600) // Increased to 600 with smart truncation for better article previews | ||
| summary: smartTruncate(htmlEscape(cleanSummary), 600) // DOMPurify removes tags, htmlEscape handles entities |
There was a problem hiding this comment.
Applying htmlEscape after DOMPurify.sanitize with ALLOWED_TAGS: [] may result in double-escaping. DOMPurify with ALLOWED_TAGS: [] already converts HTML to text content, so applying htmlEscape afterward will escape already-safe text. This could display escaped entities like & to users instead of &. Consider removing htmlEscape here or using only DOMPurify.
| // Article links for external sources | ||
| function getArticleLink(url, category = 'general') { | ||
| // Return direct link without any tracking parameters | ||
| // Users can add their own UTM if needed | ||
| return url; | ||
| } | ||
|
|
There was a problem hiding this comment.
The function getArticleLink is defined but never used in the code. Since it only returns the URL unchanged, consider removing this unused function to improve code maintainability.
| // Article links for external sources | |
| function getArticleLink(url, category = 'general') { | |
| // Return direct link without any tracking parameters | |
| // Users can add their own UTM if needed | |
| return url; | |
| } |
No description provided.