From afd582392b507931dc137da9f7d6f7d8d23d99c8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Jan 2026 19:54:22 +0000 Subject: [PATCH 1/9] chore: remove utm parameters - links now direct and clean --- src/aggregator.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/aggregator.js b/src/aggregator.js index 7a1625e1d..c6ba969bd 100644 --- a/src/aggregator.js +++ b/src/aggregator.js @@ -21,11 +21,11 @@ const FEED_CATEGORIES = { ] }; -// UTM parameters for AI-Pulse traffic tracking -// Tracks clicks sent FROM AI-Pulse TO external sites -function addUTMParams(url, category = 'general') { - const utmParams = `utm_source=ai-pulse&utm_medium=reader&utm_campaign=article&utm_content=${category}`; - return url.includes('?') ? `${url}&${utmParams}` : `${url}?${utmParams}`; +// 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; } /** @@ -69,7 +69,7 @@ function sanitizeArticle(article, sourceName, tags, category) { return { title: article.title?.replace(/<[^>]*>/g, '').slice(0, 200) || 'Untitled', - link: addUTMParams(article.link, category), // UTM tracks traffic FROM AI-Pulse + link: article.link, // Direct link, no tracking pubDate: new Date(article.pubDate || Date.now()), source: sourceName, tags: tags, From b51f51e48316c318c6440a08297b6fc287bba969 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Jan 2026 19:55:10 +0000 Subject: [PATCH 2/9] chore: replace broken github.io links with direct GitHub repository links --- src/aggregator.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/aggregator.js b/src/aggregator.js index c6ba969bd..4caef955a 100644 --- a/src/aggregator.js +++ b/src/aggregator.js @@ -135,9 +135,9 @@ function generateREADME(categorizedArticles) { **Built by [ThePhoenixAgency](https://github.com/ThePhoenixAgency)** - AI & Cybersecurity Specialist -🔥 **[View My Portfolio](https://thephoenixagency.github.io/AI-Pulse/portfolio.html)** | -📊 **[Live Stats Dashboard](https://thephoenixagency.github.io/AI-Pulse/stats.html)** | -🚀 **[Launch Reader App](https://thephoenixagency.github.io/AI-Pulse/reader.html)** +🔥 **[GitHub Repository](https://github.com/ThePhoenixAgency/AI-Pulse)** | +📊 **[Organization](https://github.com/ThePhoenixAgency)** | +🚀 **[Follow Us](https://github.com/ThePhoenixAgency)** > Passionate about building secure, privacy-first applications that make a difference. > This project showcases my expertise in full-stack development, security engineering, and data privacy. @@ -190,9 +190,9 @@ function generateREADME(categorizedArticles) { readme += `## 🧭 Navigation\n\n`; readme += `
\n\n`; readme += `### Explore AI-Pulse\n\n`; - readme += `| 🏠 [Main App](https://thephoenixagency.github.io/AI-Pulse/reader.html) | 👨‍💻 [Portfolio](https://thephoenixagency.github.io/AI-Pulse/portfolio.html) | 📊 [Stats](https://thephoenixagency.github.io/AI-Pulse/stats.html) | 📚 [Docs](./database/SUPABASE_MIGRATION.md) |\n`; - readme += `|:---:|:---:|:---:|:---:|\n`; - readme += `| Read articles in-app | View my projects | Analytics dashboard | Migration guide |\n\n`; + readme += `| 📚 [Repository](https://github.com/ThePhoenixAgency/AI-Pulse) | 👨‍💻 [Organization](https://github.com/ThePhoenixAgency) | 🔐 [Docs](./database/SUPABASE_MIGRATION.md) |\n`; + readme += `|:---:|:---:|:---:|\n`; + readme += `| Source Code | Team Profile | Technical Docs |\n\n`; readme += `---\n\n`; readme += `### 🤝 Connect With Me\n\n`; readme += `[![GitHub Profile](https://img.shields.io/badge/GitHub-EthanThePhoenix38-181717?style=for-the-badge&logo=github)](https://github.com/EthanThePhoenix38)\n`; From 1a30a580988f8cab4abc103bece457ec28d061b0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Jan 2026 19:56:18 +0000 Subject: [PATCH 3/9] fix: remove security vulnerabilities - no external shields, no personal data exposure, safe markdown links only --- src/aggregator.js | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/aggregator.js b/src/aggregator.js index 4caef955a..3d2b5ac8c 100644 --- a/src/aggregator.js +++ b/src/aggregator.js @@ -121,9 +121,7 @@ function generateREADME(categorizedArticles) { > Curated content from the best sources - Auto-updated every 6 hours -[![Auto Update](https://img.shields.io/badge/Auto--Update-Every%206h-blueviolet?style=for-the-badge)](https://github.com/ThePhoenixAgency/AI-Pulse) -[![Articles](https://img.shields.io/badge/Fresh-Articles-blue?style=for-the-badge)](https://github.com/ThePhoenixAgency/AI-Pulse) -[![Open Source](https://img.shields.io/badge/100%25-Open%20Source-success?style=for-the-badge)](https://github.com/ThePhoenixAgency/AI-Pulse) +📅 **Auto-updated every 6 hours** | ✨ **Fresh AI & Cybersecurity news** | 🔓 **100% Open Source** **Last Update:** ${new Date().toUTCString()} @@ -144,20 +142,20 @@ function generateREADME(categorizedArticles) { ### 🛠️ Tech Stack -![Node.js](https://img.shields.io/badge/Node.js-18+-339933?style=flat-square&logo=node.js&logoColor=white) -![JavaScript](https://img.shields.io/badge/JavaScript-ES6+-F7DF1E?style=flat-square&logo=javascript&logoColor=black) -![DOMPurify](https://img.shields.io/badge/DOMPurify-3.0+-blue?style=flat-square) -![Express](https://img.shields.io/badge/Express-4.18+-000000?style=flat-square&logo=express&logoColor=white) -![Supabase](https://img.shields.io/badge/Supabase-Ready-3ECF8E?style=flat-square&logo=supabase&logoColor=white) +- **Node.js** 20.x runtime +- **JavaScript** ES6+ with security-first design +- **DOMPurify** 3.0+ for XSS protection +- **Express** 4.18+ server framework +- **Supabase** PostgreSQL with RLS enabled ### 🔒 Security & Compliance -![XSS Protection](https://img.shields.io/badge/XSS-Protected-success?style=flat-square&logo=security&logoColor=white) -![0 CVE](https://img.shields.io/badge/CVE-0%20Known-success?style=flat-square) -![GDPR](https://img.shields.io/badge/GDPR-Compliant-blue?style=flat-square) -![RLS](https://img.shields.io/badge/RLS-Enabled-blueviolet?style=flat-square) -![Dependabot](https://img.shields.io/badge/Dependabot-Auto--Merge-green?style=flat-square&logo=dependabot&logoColor=white) -![Anonymous Analytics](https://img.shields.io/badge/Analytics-Anonymous%20Only-orange?style=flat-square) +- ✅ **XSS Protection** - DOMPurify sanitization +- ✅ **0 Known CVEs** - Verified dependencies +- ✅ **GDPR Compliant** - Privacy-focused +- ✅ **RLS Enabled** - Row-Level Security on database +- ✅ **Dependabot** - Automated security updates 2x daily +- ✅ **Anonymous Analytics** - No tracking, privacy first
@@ -195,9 +193,7 @@ function generateREADME(categorizedArticles) { readme += `| Source Code | Team Profile | Technical Docs |\n\n`; readme += `---\n\n`; readme += `### 🤝 Connect With Me\n\n`; - readme += `[![GitHub Profile](https://img.shields.io/badge/GitHub-EthanThePhoenix38-181717?style=for-the-badge&logo=github)](https://github.com/EthanThePhoenix38)\n`; - readme += `[![Organization](https://img.shields.io/badge/Organization-ThePhoenixAgency-181717?style=for-the-badge&logo=github)](https://github.com/ThePhoenixAgency)\n`; - readme += `[![Website](https://img.shields.io/badge/Website-ThePhoenixAgency.github.io-blue?style=for-the-badge&logo=google-chrome&logoColor=white)](https://ThePhoenixAgency.github.io)\n\n`; + readme += `[GitHub Organization](https://github.com/ThePhoenixAgency) | [Repository](https://github.com/ThePhoenixAgency/AI-Pulse) | [Issues & Support](https://github.com/ThePhoenixAgency/AI-Pulse/issues)\n\n`; readme += `---\n\n`; readme += `*Powered by [AI-Pulse](https://github.com/ThePhoenixAgency/AI-Pulse) | 100% Free & Open Source | Built with ❤️ by ThePhoenixAgency*\n\n`; readme += `\n`; From 0d809b9bfebf0adf68ee06f605206d555ea1bc26 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Jan 2026 19:56:56 +0000 Subject: [PATCH 4/9] fix: restore shields.io badges - they are needed --- src/aggregator.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/aggregator.js b/src/aggregator.js index 3d2b5ac8c..0e45935a1 100644 --- a/src/aggregator.js +++ b/src/aggregator.js @@ -121,7 +121,9 @@ function generateREADME(categorizedArticles) { > Curated content from the best sources - Auto-updated every 6 hours -📅 **Auto-updated every 6 hours** | ✨ **Fresh AI & Cybersecurity news** | 🔓 **100% Open Source** +[![Auto Update](https://img.shields.io/badge/Auto--Update-Every%206h-blueviolet?style=for-the-badge)](https://github.com/ThePhoenixAgency/AI-Pulse) +[![Articles](https://img.shields.io/badge/Fresh-Articles-blue?style=for-the-badge)](https://github.com/ThePhoenixAgency/AI-Pulse) +[![Open Source](https://img.shields.io/badge/100%25-Open%20Source-success?style=for-the-badge)](https://github.com/ThePhoenixAgency/AI-Pulse) **Last Update:** ${new Date().toUTCString()} @@ -142,20 +144,20 @@ function generateREADME(categorizedArticles) { ### 🛠️ Tech Stack -- **Node.js** 20.x runtime -- **JavaScript** ES6+ with security-first design -- **DOMPurify** 3.0+ for XSS protection -- **Express** 4.18+ server framework -- **Supabase** PostgreSQL with RLS enabled +![Node.js](https://img.shields.io/badge/Node.js-18+-339933?style=flat-square&logo=node.js&logoColor=white) +![JavaScript](https://img.shields.io/badge/JavaScript-ES6+-F7DF1E?style=flat-square&logo=javascript&logoColor=black) +![DOMPurify](https://img.shields.io/badge/DOMPurify-3.0+-blue?style=flat-square) +![Express](https://img.shields.io/badge/Express-4.18+-000000?style=flat-square&logo=express&logoColor=white) +![Supabase](https://img.shields.io/badge/Supabase-Ready-3ECF8E?style=flat-square&logo=supabase&logoColor=white) ### 🔒 Security & Compliance -- ✅ **XSS Protection** - DOMPurify sanitization -- ✅ **0 Known CVEs** - Verified dependencies -- ✅ **GDPR Compliant** - Privacy-focused -- ✅ **RLS Enabled** - Row-Level Security on database -- ✅ **Dependabot** - Automated security updates 2x daily -- ✅ **Anonymous Analytics** - No tracking, privacy first +![XSS Protection](https://img.shields.io/badge/XSS-Protected-success?style=flat-square&logo=security&logoColor=white) +![0 CVE](https://img.shields.io/badge/CVE-0%20Known-success?style=flat-square) +![GDPR](https://img.shields.io/badge/GDPR-Compliant-blue?style=flat-square) +![RLS](https://img.shields.io/badge/RLS-Enabled-blueviolet?style=flat-square) +![Dependabot](https://img.shields.io/badge/Dependabot-Auto--Merge-green?style=flat-square&logo=dependabot&logoColor=white) +![Anonymous Analytics](https://img.shields.io/badge/Analytics-Anonymous%20Only-orange?style=flat-square) @@ -193,7 +195,9 @@ function generateREADME(categorizedArticles) { readme += `| Source Code | Team Profile | Technical Docs |\n\n`; readme += `---\n\n`; readme += `### 🤝 Connect With Me\n\n`; - readme += `[GitHub Organization](https://github.com/ThePhoenixAgency) | [Repository](https://github.com/ThePhoenixAgency/AI-Pulse) | [Issues & Support](https://github.com/ThePhoenixAgency/AI-Pulse/issues)\n\n`; + readme += `[![GitHub Profile](https://img.shields.io/badge/GitHub-ThePhoenixAgency-181717?style=for-the-badge&logo=github)](https://github.com/ThePhoenixAgency)\n`; + readme += `[![Repository](https://img.shields.io/badge/Repository-AI--Pulse-181717?style=for-the-badge&logo=github)](https://github.com/ThePhoenixAgency/AI-Pulse)\n`; + readme += `[![Support](https://img.shields.io/badge/Support-Issues-181717?style=for-the-badge&logo=github)](https://github.com/ThePhoenixAgency/AI-Pulse/issues)\n\n`; readme += `---\n\n`; readme += `*Powered by [AI-Pulse](https://github.com/ThePhoenixAgency/AI-Pulse) | 100% Free & Open Source | Built with ❤️ by ThePhoenixAgency*\n\n`; readme += `\n`; From 538147233e5c052d77985511332d256ed0f9f8ce Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Jan 2026 19:58:36 +0000 Subject: [PATCH 5/9] security: fix XSS vulnerability - properly escape HTML entities in article titles and summaries --- src/aggregator.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/aggregator.js b/src/aggregator.js index 0e45935a1..006dda31f 100644 --- a/src/aggregator.js +++ b/src/aggregator.js @@ -28,6 +28,22 @@ function getArticleLink(url, category = 'general') { return url; } +/** + * HTML-escape a string so it is safe to insert into HTML contexts. + * Converts &, <, and > to their corresponding entities. + * @param {string} input + * @returns {string} + */ +function htmlEscape(input) { + if (!input) { + return ''; + } + return input + .replace(/&/g, '&') + .replace(//g, '>'); +} + /** * Smart truncate: cut at last punctuation before limit * Avoids cutting words in the middle @@ -65,10 +81,12 @@ function smartTruncate(text, maxLength = 500) { // Sanitize and process articles function sanitizeArticle(article, sourceName, tags, category) { - const rawSummary = article.contentSnippet?.replace(/<[^>]*>/g, '') || ''; + const rawSummary = htmlEscape( + article.contentSnippet?.replace(/<[^>]*>/g, '') || '' + ); return { - title: article.title?.replace(/<[^>]*>/g, '').slice(0, 200) || 'Untitled', + title: htmlEscape(article.title?.replace(/<[^>]*>/g, '') || '').slice(0, 200) || 'Untitled', link: article.link, // Direct link, no tracking pubDate: new Date(article.pubDate || Date.now()), source: sourceName, From 6c244e30a44aaef45d42cfc09f314bd840b205a1 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Jan 2026 19:59:11 +0000 Subject: [PATCH 6/9] security: use DOMPurify instead of regex for bulletproof HTML sanitization --- src/aggregator.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/aggregator.js b/src/aggregator.js index 006dda31f..97ab5d9eb 100644 --- a/src/aggregator.js +++ b/src/aggregator.js @@ -2,6 +2,7 @@ const Parser = require('rss-parser'); const axios = require('axios'); const { Octokit } = require('@octokit/rest'); +const DOMPurify = require('isomorphic-dompurify'); const parser = new Parser({ timeout: 10000, @@ -81,18 +82,18 @@ function smartTruncate(text, maxLength = 500) { // Sanitize and process articles function sanitizeArticle(article, sourceName, tags, category) { - const rawSummary = htmlEscape( - article.contentSnippet?.replace(/<[^>]*>/g, '') || '' - ); + // 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: htmlEscape(article.title?.replace(/<[^>]*>/g, '') || '').slice(0, 200) || 'Untitled', + 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 }; } From fb2f158b923bb5602046041b34127db44f3b8fac Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Jan 2026 19:59:55 +0000 Subject: [PATCH 7/9] security: properly validate article URL before iframe assignment - prevent XSS --- reader.html | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/reader.html b/reader.html index 7ca7e837d..b5496d8bb 100644 --- a/reader.html +++ b/reader.html @@ -440,7 +440,7 @@

Impossible de charger l'article

// Parse URL Parameters const params = new URLSearchParams(window.location.search); - const articleUrl = params.get('url'); + const rawArticleUrl = params.get('url'); const articleTitle = params.get('title'); const articleSource = params.get('source'); const articleTags = params.get('tags'); @@ -456,8 +456,14 @@

Impossible de charger l'article

} } + // Validate and sanitize URL - only proceed if URL is safe + let articleUrl = null; + if (rawArticleUrl && isValidHttpUrl(rawArticleUrl)) { + articleUrl = rawArticleUrl; // Now articleUrl is guaranteed to be a valid http(s) URL + } + // If article parameters exist and URL is valid, show article view - if (articleUrl && isValidHttpUrl(articleUrl)) { + if (articleUrl) { showView('article'); // Show article tab and external link button From 3495b87b844c6395b24e74bcd05470ecdf79d080 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Jan 2026 20:00:19 +0000 Subject: [PATCH 8/9] security: use crypto.getRandomValues() instead of Math.random() for session ID generation --- tracker.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tracker.js b/tracker.js index e02cfc358..12fa8ba23 100644 --- a/tracker.js +++ b/tracker.js @@ -44,10 +44,15 @@ class AIPlulseTracker { } /** - * Generate a unique session ID (not personally identifiable) + * Generate a unique session ID using cryptographically secure randomness + * (not personally identifiable) */ generateSessionId() { - return 'sess_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); + // Use crypto.getRandomValues for secure randomness instead of Math.random() + const randomBytes = new Uint8Array(6); + crypto.getRandomValues(randomBytes); + const randomHex = Array.from(randomBytes).map(b => b.toString(16).padStart(2, '0')).join(''); + return 'sess_' + Date.now() + '_' + randomHex; } /** From eb870d7ca1b745f919fb01a9aa0671427362e146 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Jan 2026 20:03:55 +0000 Subject: [PATCH 9/9] chore: add security guidelines, vercel config, package.json metadata, and CSP headers --- .claude/security-guidelines.md | 178 +++++++++++++++++++++++++++++++++ package.json | 15 +++ reader.html | 1 + vercel.json | 66 ++++++++++++ 4 files changed, 260 insertions(+) create mode 100644 .claude/security-guidelines.md create mode 100644 vercel.json diff --git a/.claude/security-guidelines.md b/.claude/security-guidelines.md new file mode 100644 index 000000000..bb85a51c3 --- /dev/null +++ b/.claude/security-guidelines.md @@ -0,0 +1,178 @@ +# 🔒 AI-Pulse Security Guidelines for Claude Code + +## Purpose +These guidelines prevent common security vulnerabilities from being introduced in AI-Pulse. Follow these rules when writing code. + +--- + +## 1. INPUT SANITIZATION & XSS PREVENTION + +### ❌ DO NOT: +- Use simple regex `.replace(/<[^>]*>/g, '')` to sanitize HTML +- Directly insert user input into `innerHTML`, `eval()`, or script content +- Trust URL query parameters without validation +- Use `Math.random()` for security-sensitive values + +### ✅ DO: +- **Always use DOMPurify** for HTML sanitization: + ```js + const clean = DOMPurify.sanitize(userInput, { ALLOWED_TAGS: [] }); + ``` +- **Escape HTML entities** after DOMPurify: + ```js + function htmlEscape(input) { + return input + .replace(/&/g, '&') + .replace(//g, '>'); + } + ``` +- **Validate URLs before iframe.src**: + ```js + function isValidHttpUrl(string) { + try { + const url = new URL(string); + return url.protocol === 'http:' || url.protocol === 'https:'; + } catch (_) { + return false; + } + } + ``` + +--- + +## 2. CRYPTOGRAPHIC OPERATIONS + +### ❌ DO NOT: +- Use `Math.random()` for session IDs, tokens, or any security value +- Generate predictable values with timestamps alone + +### ✅ DO: +- Use `crypto.getRandomValues()` for random values: + ```js + const randomBytes = new Uint8Array(6); + crypto.getRandomValues(randomBytes); + const randomHex = Array.from(randomBytes) + .map(b => b.toString(16).padStart(2, '0')) + .join(''); + ``` + +--- + +## 3. SECRET MANAGEMENT + +### ❌ DO NOT: +- Hardcode emails, API keys, or credentials in code +- Expose personal data in README or comments +- Commit `.env` files or private keys + +### ✅ DO: +- **Use GitHub Secrets** for: + - `GIT_AUTHOR_EMAIL` → phoenix.project@outlook.fr + - `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET` + - `LINKEDIN_ACCESS_TOKEN`, `LINKEDIN_USER_ID` + - `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY` +- **Check .gitignore** excludes: + - `.env*` (except .env.example) + - `*.key`, `*.pem` + - `**/credentials/`, `**/secrets/` + +--- + +## 4. DEPENDENCY VERSIONS + +### ❌ DO NOT: +- Use unversioned dependencies (`latest`) +- Ignore security warnings from npm audit + +### ✅ DO: +- Pin versions in package.json +- Use Dependabot for automated updates +- Review CVE alerts from CodeQL + +--- + +## 5. URL HANDLING + +### ❌ DO NOT: +- Use user input directly in: + - `window.location` + - `iframe.src` + - `fetch()` URLs + - Redirect targets + +### ✅ DO: +- Validate URLs with `isValidHttpUrl()` BEFORE use +- Use relative URLs when possible +- Explicitly assign only after validation: + ```js + let safeUrl = null; + if (userUrl && isValidHttpUrl(userUrl)) { + safeUrl = userUrl; + } + iframe.src = safeUrl; // Now safe to use + ``` + +--- + +## 6. CODE REVIEW CHECKLIST + +Before submitting any code, verify: + +- [ ] No regex-only HTML sanitization (use DOMPurify) +- [ ] No hardcoded secrets or emails +- [ ] No `Math.random()` for security values +- [ ] URLs validated before iframe/redirect +- [ ] No `innerHTML` with unsanitized input +- [ ] All user input escaped with `htmlEscape()` +- [ ] No exposed personal data in comments +- [ ] `.gitignore` includes all sensitive patterns +- [ ] Dependabot configured (2x daily updates) +- [ ] CodeQL alerts reviewed and fixed + +--- + +## 7. KNOWN VULNERABILITIES (DO NOT REPEAT) + +### Previously Fixed: +1. **Incomplete Multi-Character Sanitization** (aggregator.js:68) + - Fixed with: DOMPurify + htmlEscape + +2. **Unvalidated URL Redirection** (reader.html:496) + - Fixed with: Explicit validation before iframe.src assignment + +3. **Insecure Randomness** (tracker.js:8) + - Fixed with: crypto.getRandomValues() + +--- + +## 8. TOOLS & RESOURCES + +- **DOMPurify**: Sanitize HTML safely +- **crypto API**: Secure random generation +- **CodeQL**: Detect XSS, injection, randomness issues +- **GitHub Secrets**: Store sensitive values +- **Dependabot**: Automate dependency updates + +--- + +## 9. DEPLOYMENT SECURITY + +### For Vercel: +- [ ] All secrets in Vercel Environment Variables +- [ ] No .env files in repo (use .env.example) +- [ ] ALLOWED_TAGS in DOMPurify explicitly set to [] +- [ ] Content-Security-Policy headers configured +- [ ] CORS properly restricted + +--- + +## Questions? + +If unsure about security implications: +1. Ask this guide first +2. Check CodeQL rules +3. Consult OWASP Top 10 +4. Run `npm audit` + +**Remember**: Security is not a feature, it's a requirement. diff --git a/package.json b/package.json index 332a087ac..02b8e2420 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,16 @@ { + "name": "ai-pulse", + "version": "1.0.0", + "description": "Curated AI & Cybersecurity news aggregator", + "main": "src/aggregator.js", + "scripts": { + "start": "node src/aggregator.js", + "dev": "node src/aggregator.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": ["ai", "cybersecurity", "news", "aggregator", "rss"], + "author": "ThePhoenixAgency", + "license": "MIT", "dependencies": { "@octokit/rest": "^22.0.1", "axios": "^1.13.2", @@ -8,5 +20,8 @@ "express": "^5.2.1", "isomorphic-dompurify": "^2.35.0", "rss-parser": "^3.13.0" + }, + "engines": { + "node": ">=20.0.0" } } diff --git a/reader.html b/reader.html index b5496d8bb..611dc6b0e 100644 --- a/reader.html +++ b/reader.html @@ -3,6 +3,7 @@ + AI-Pulse Reader