-
-
Notifications
You must be signed in to change notification settings - Fork 1
Claude/audit dependabot setup xfl71 #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
afd5823
b51f51e
1a30a58
0d809b9
5381472
6c244e3
fb2f158
3495b87
eb870d7
0cce006
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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, '<') | ||
| .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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
| <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:"> | ||
|
||
| <title>AI-Pulse Reader</title> | ||
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | ||
| <style> | ||
|
|
@@ -440,7 +441,7 @@ <h2>Impossible de charger l'article</h2> | |
|
|
||
| // 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,6 +457,12 @@ <h2>Impossible de charger l'article</h2> | |
| } | ||
| } | ||
|
|
||
| // 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)) { | ||
| const safeArticleUrl = new URL(articleUrl).toString(); | ||
|
|
@@ -472,7 +479,6 @@ <h2>Impossible de charger l'article</h2> | |
| } catch (_) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| // If article parameters exist and URL is valid and allowed, show article view | ||
| if (articleUrl && isAllowedArticleUrl(articleUrl)) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,11 +22,27 @@ 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; | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
|
Comment on lines
+25
to
+31
|
||||||||||||||
| // 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; | |
| } |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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, ''')
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,66 @@ | ||||||||||
| { | ||||||||||
| "version": 2, | ||||||||||
| "name": "ai-pulse", | ||||||||||
| "builds": [ | ||||||||||
| { | ||||||||||
| "src": "src/aggregator.js", | ||||||||||
| "use": "@vercel/node" | ||||||||||
| }, | ||||||||||
| { | ||||||||||
| "src": "*.html", | ||||||||||
| "use": "@vercel/static" | ||||||||||
| }, | ||||||||||
| { | ||||||||||
| "src": "*.js", | ||||||||||
|
||||||||||
| "src": "*.js", | |
| "src": "public/*.js", |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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'" |
Copilot
AI
Jan 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new CSP restricts
script-srcto'self', butreader.htmlstill loads DOMPurify fromhttps://cdn.jsdelivr.net/...(and gtag fromhttps://www.googletagmanager.com). Browsers enforcing CSP will block those scripts, so when the inline script later callsDOMPurify.sanitize(...)it throws aReferenceErrorand breaks the article view for typical URLs that includesource/tags. Consider whitelisting the required CDN origins inscript-srcor self-hosting those scripts so the sanitizer is available.Useful? React with 👍 / 👎.