Skip to content
Merged
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
19 changes: 17 additions & 2 deletions src/aggregator.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const Parser = require('rss-parser');
const axios = require('axios');
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

Unused variable axios.

Suggested change
const axios = require('axios');

Copilot uses AI. Check for mistakes.
const { Octokit } = require('@octokit/rest');
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

Unused variable Octokit.

Suggested change
const { Octokit } = require('@octokit/rest');

Copilot uses AI. Check for mistakes.
const sanitizeHtml = require('sanitize-html');
const { URL } = require('url');

const parser = new Parser({
timeout: 10000,
Expand Down Expand Up @@ -33,8 +34,22 @@ const FEED_CATEGORIES = {
// Tracks clicks sent FROM AI-Pulse TO external sites
function addUTMParams(url, category = 'general') {
// Use Freedium mirror for Medium articles to bypass paywall
if (url.includes('medium.com') || url.includes('towardsdatascience.com')) {
url = `https://freedium.cloud/${url}`;
try {
const parsed = new URL(url);
const hostname = parsed.hostname.toLowerCase();
const mediumHosts = [
'medium.com',
'www.medium.com',
'towardsdatascience.com',
'www.towardsdatascience.com'
];
Comment on lines +40 to +45
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

The mediumHosts array is defined inside the function on every call. Consider moving this to a constant at the module level (similar to FEED_CATEGORIES) to avoid unnecessary array creation on each invocation, improving performance and maintainability.

Copilot uses AI. Check for mistakes.

if (mediumHosts.includes(hostname)) {
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

The allowlist only includes exact domains but doesn't handle Medium's custom subdomain pattern. Medium allows users to publish on custom subdomains (e.g., username.medium.com or publication.medium.com). Consider checking if the hostname ends with .medium.com to catch all legitimate Medium subdomains, while still preventing path-based attacks.

Suggested change
if (mediumHosts.includes(hostname)) {
if (mediumHosts.includes(hostname) || hostname.endsWith('.medium.com')) {

Copilot uses AI. Check for mistakes.
// Rewrite only genuine Medium/TDS URLs to Freedium
url = `https://freedium.cloud/${url}`;
}
} catch (e) {
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

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

The error handling silently swallows all exceptions without logging. This makes debugging difficult if URL parsing fails. Consider logging the error (at minimum during development) or documenting why silent failure is acceptable here. A malformed URL error could indicate a data quality issue that should be investigated.

Suggested change
} catch (e) {
} catch (e) {
if (process.env.NODE_ENV !== 'production') {
console.error('addUTMParams: Failed to parse URL for UTM handling:', url, e);
}

Copilot uses AI. Check for mistakes.
// If URL parsing fails, skip Freedium rewrite and just append UTM params
}

const utmParams = `utm_source=ai-pulse&utm_medium=reader&utm_campaign=article&utm_content=${category}`;
Expand Down
Loading