Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/update-ai-pulse.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ jobs:
npm install axios cheerio rss-parser @octokit/rest
Copy link

Copilot AI Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow is missing the sanitize-html dependency in the install step. The code at line 5 of src/aggregator.js requires sanitize-html, but the npm install command only includes axios, cheerio, rss-parser, and @octokit/rest. This will cause the workflow to fail with a module not found error. Consider either adding sanitize-html to the install command or replacing line 29 with npm install to install all dependencies from package.json.

Copilot uses AI. Check for mistakes.

- name: Aggregate AI and Cybersecurity articles
env:
LINKEDIN_ACCESS_TOKEN: ${{ secrets.LINKEDIN_ACCESS_TOKEN }}
LINKEDIN_USER_ID: ${{ secrets.LINKEDIN_USER_ID }}
# LinkedIn integration temporarily disabled
# env:
# LINKEDIN_ACCESS_TOKEN: ${{ secrets.LINKEDIN_ACCESS_TOKEN }}
# LINKEDIN_USER_ID: ${{ secrets.LINKEDIN_USER_ID }}
run: |
node src/aggregator.js | tee NEW-README.md

Expand Down
9 changes: 8 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,10 @@
"dompurify": "^3.3.1",
"express": "^5.2.1",
"isomorphic-dompurify": "^2.35.0",
"rss-parser": "^3.13.0"
"rss-parser": "^3.13.0",
"sanitize-html": "^2.17.0"
},
"engines": {
"node": ">=20.0.0"
"rss-parser": "^3.13.0",
"sanitize-html": "^2.17.0"
}
}
26 changes: 2 additions & 24 deletions src/aggregator.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,40 +91,18 @@ function smartTruncate(text, maxLength = 500) {
return truncated.trim() + '...';
}

/**
* 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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}

// Sanitize and process articles
function sanitizeArticle(article, sourceName, tags, category) {
const rawSummary = htmlEscape(
article.contentSnippet?.replace(/<[^>]*>/g, '') || ''
);

return {
title: htmlEscape(article.title?.replace(/<[^>]*>/g, '') || '').slice(0, 200) || 'Untitled',
const rawSummary = sanitizeText(article.contentSnippet) || '';

return {
title: (sanitizeText(article.title) || 'Untitled').slice(0, 200),
link: addUTMParams(article.link, category), // UTM tracks traffic FROM AI-Pulse
link: getArticleLink(article.link, category), // Direct link without UTM tracking
pubDate: new Date(article.pubDate || Date.now()),
source: sourceName,
tags: tags,
category: article.categories?.[0] || 'General',
summary: smartTruncate(htmlEscape(cleanSummary), 600) // DOMPurify removes tags, htmlEscape handles entities
summary: smartTruncate(rawSummary, 600)
};
}

Expand Down
Loading