Conversation
…(POC) - Transform generic link list into comprehensive 3,551-word analytical article - Add unique title: 'Weapons Law and VAT Fraud: Tidö Priorities This Week' - Provide 200-400 word analysis per proposition (10 total) - Include policy details, political context, implementation timelines - Add cross-cutting analysis on government strategic priorities - Create 'What to Watch' section with forward-looking insights - Update all metadata (Schema.org, OG, Twitter Card) - Pass HTML validation (htmlhint) Proof-of-concept for enhancing 58 government propositions articles. See PROPOSITION_ENHANCEMENT_PLAN.md for full implementation roadmap. Content quality matches 2026-02-13-evening-analysis-en.html reference. 5x word count increase demonstrates feasibility of comprehensive analysis.
- Document proof-of-concept achievements - Provide complete implementation roadmap - Detail MCP data integration approach - Outline translation strategy options - Track progress metrics (1.7% complete) Ready for Phase 1 continuation in next session.
- Demonstrate 5x word count increase (711 → 3,551 words) - Showcase quality improvements (generic → analytical) - Document SEO enhancements (unique titles, specific descriptions) - Compare content structures (3 sections → 11 sections) - Outline replication template for remaining 57 articles Visual proof of concept success and transformation impact.
…h comprehensive documentation Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
…nced Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
|
|
||
| // Update language switcher active state | ||
| // Remove 'active' from the English link without changing its href | ||
| const removeEnActivePattern = new RegExp(`(<a href="${datePattern}-government-propositions-en\\.html" class="lang-link) active"`, 'g'); |
Check failure
Code scanning / CodeQL
Regular expression injection High
Copilot Autofix
AI about 17 hours ago
Copilot could not generate an autofix suggestion
Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.
| `$1"` | ||
| ); | ||
| // Add 'active' to the current target language link without changing its href | ||
| const addTargetActivePattern = new RegExp(`(<a href="${datePattern}-government-propositions-${targetLang}\\.html" class="lang-link)(?! active")`, 'g'); |
Check failure
Code scanning / CodeQL
Regular expression injection High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 17 hours ago
In general, to fix regular expression injection you must ensure that any untrusted data interpolated into a regex pattern is first escaped so that it is treated as literal text, not as regex syntax. In Node.js, a common approach is to use a library function like _.escapeRegExp from lodash, or to implement a small local helper that replaces all regex metacharacters with escaped versions (e.g., replacing . with \.).
For this script, the only tainted value entering a RegExp is datePattern in the patterns on lines 627, 633, and 646. Although datePattern is currently constrained to a date format, we can make the code robust and satisfy CodeQL by escaping datePattern before embedding it into any regex templates. To avoid adding external dependencies across the project, we can define a small local helper function escapeRegExp near the other top-level constants in scripts/translate-propositions.cjs. This helper will escape all regex metacharacters. We then replace uses of datePattern inside new RegExp(...) with an escaped variant, say escapedDatePattern.
Concretely:
- Add a function
escapeRegExp(string)near the top of the file (after the existingrequirecalls) that escapes[-\/\\^$*+?.()|[\]{}]. - After computing
datePatternon line 623, computeconst escapedDatePattern = escapeRegExp(datePattern);. - In the three
new RegExpcalls (lines 627, 633, and 646), interpolateescapedDatePatterninstead ofdatePattern. - No behavior change occurs for properly formatted dates (which do not contain regex metacharacters apart from
-that is safely escaped), but the code becomes safe even if the filename format changes or is misused.
| @@ -25,6 +25,11 @@ | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
|
|
||
| // Escape special characters in a string to be used as a literal in a RegExp | ||
| function escapeRegExp(string) { | ||
| return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); | ||
| } | ||
|
|
||
| // Translation dictionaries for all 14 languages | ||
| const translations = { | ||
| sv: { | ||
| @@ -621,16 +626,17 @@ | ||
| // e.g., "2026-02-18" from "2026-02-18-government-propositions-en.html" | ||
| const dateMatch = path.basename(sourceFile).match(/(\d{4}-\d{2}-\d{2})/); | ||
| const datePattern = dateMatch ? dateMatch[1] : '2026-02-18'; // fallback if no date found | ||
| const escapedDatePattern = escapeRegExp(datePattern); | ||
|
|
||
| // Update language switcher active state | ||
| // Remove 'active' from the English link without changing its href | ||
| const removeEnActivePattern = new RegExp(`(<a href="${datePattern}-government-propositions-en\\.html" class="lang-link) active"`, 'g'); | ||
| const removeEnActivePattern = new RegExp(`(<a href="${escapedDatePattern}-government-propositions-en\\.html" class="lang-link) active"`, 'g'); | ||
| translated = translated.replace( | ||
| removeEnActivePattern, | ||
| `$1"` | ||
| ); | ||
| // Add 'active' to the current target language link without changing its href | ||
| const addTargetActivePattern = new RegExp(`(<a href="${datePattern}-government-propositions-${targetLang}\\.html" class="lang-link)(?! active")`, 'g'); | ||
| const addTargetActivePattern = new RegExp(`(<a href="${escapedDatePattern}-government-propositions-${targetLang}\\.html" class="lang-link)(?! active")`, 'g'); | ||
| translated = translated.replace( | ||
| addTargetActivePattern, | ||
| `$1 active"` | ||
| @@ -643,7 +641,7 @@ | ||
| ); | ||
|
|
||
| // Update canonical in URLs | ||
| const urlPattern = new RegExp(`(${datePattern}-government-propositions-)en(\\.html)`, 'g'); | ||
| const urlPattern = new RegExp(`(${escapedDatePattern}-government-propositions-)en(\\.html)`, 'g'); | ||
| translated = translated.replace(urlPattern, `$1${targetLang}$2`); | ||
|
|
||
| // Write output |
| ); | ||
|
|
||
| // Update canonical in URLs | ||
| const urlPattern = new RegExp(`(${datePattern}-government-propositions-)en(\\.html)`, 'g'); |
Check failure
Code scanning / CodeQL
Regular expression injection High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 17 hours ago
General fix: when constructing a regular expression from any value derived from user input (including command-line arguments), ensure that the interpolated portion is either (a) strictly validated to conform to a safe, limited character set, or (b) passed through a regex-escaping function so any metacharacters lose special meaning.
Best fix here without changing functionality: keep the existing extraction of the date from the filename, but add a validation step to ensure datePattern really is a valid YYYY-MM-DD date consisting only of digits and dashes. If validation fails, fall back to the existing hard-coded default ('2026-02-18'). This guarantees that datePattern is safe to embed into the regex and silences the CodeQL concern while preserving current behavior for valid inputs. We do not need external libraries; a simple additional RegExp test on datePattern suffices.
Concretely, in scripts/translate-propositions.cjs:
- Replace the current two lines:
622: const dateMatch = path.basename(sourceFile).match(/(\d{4}-\d{2}-\d{2})/);
623: const datePattern = dateMatch ? dateMatch[1] : '2026-02-18'; // fallback if no date foundwith logic that:
- Extracts the match as before.
- Validates with a strict regex
^\d{4}-\d{2}-\d{2}$. - Uses the fallback if validation fails.
No new imports or helper functions are required.
| @@ -620,7 +620,10 @@ | ||
| // Extract the date pattern from the source filename | ||
| // e.g., "2026-02-18" from "2026-02-18-government-propositions-en.html" | ||
| const dateMatch = path.basename(sourceFile).match(/(\d{4}-\d{2}-\d{2})/); | ||
| const datePattern = dateMatch ? dateMatch[1] : '2026-02-18'; // fallback if no date found | ||
| const extractedDate = dateMatch ? dateMatch[1] : null; | ||
| const datePattern = extractedDate && /^\d{4}-\d{2}-\d{2}$/.test(extractedDate) | ||
| ? extractedDate | ||
| : '2026-02-18'; // fallback if no valid date found | ||
|
|
||
| // Update language switcher active state | ||
| // Remove 'active' from the English link without changing its href |
scripts/translate-propositions.cjs
Outdated
| const newsArticleStart = translated.indexOf('"@type": "NewsArticle"'); | ||
| if (newsArticleStart !== -1) { | ||
| const descStart = translated.indexOf('"description":', newsArticleStart); | ||
| if (descStart !== -1) { | ||
| const descValueStart = translated.indexOf('"', descStart + 14) + 1; | ||
| const descValueEnd = translated.indexOf('"', descValueStart); | ||
| if (descValueEnd !== -1 && descValueEnd < translated.indexOf('"@type":', newsArticleStart + 100)) { | ||
| translated = translated.substring(0, descValueStart) + trans.description + translated.substring(descValueEnd); | ||
| } |
There was a problem hiding this comment.
The JSON-LD description replacement logic has a fragile assumption. Line 533 checks if descValueEnd < translated.indexOf('"@type":', newsArticleStart + 100), which assumes the next @type field appears within 100 characters after the NewsArticle start. This could fail if the NewsArticle JSON-LD structure has more properties or different ordering.
Consider a more robust approach:
- Find the closing brace of the NewsArticle object by counting braces
- Or use a proper JSON parser to extract and modify just the NewsArticle block
- Or at minimum, increase the offset to something more reliable (e.g., 500 characters) and add bounds checking
The current implementation might work for the existing HTML structure but could break if the JSON-LD format changes.
| const newsArticleStart = translated.indexOf('"@type": "NewsArticle"'); | |
| if (newsArticleStart !== -1) { | |
| const descStart = translated.indexOf('"description":', newsArticleStart); | |
| if (descStart !== -1) { | |
| const descValueStart = translated.indexOf('"', descStart + 14) + 1; | |
| const descValueEnd = translated.indexOf('"', descValueStart); | |
| if (descValueEnd !== -1 && descValueEnd < translated.indexOf('"@type":', newsArticleStart + 100)) { | |
| translated = translated.substring(0, descValueStart) + trans.description + translated.substring(descValueEnd); | |
| } | |
| const newsArticleTypeIndex = translated.indexOf('"@type": "NewsArticle"'); | |
| if (newsArticleTypeIndex !== -1) { | |
| // Find the start of the NewsArticle object by locating the nearest opening brace before "@type" | |
| const newsArticleObjectStart = translated.lastIndexOf('{', newsArticleTypeIndex); | |
| if (newsArticleObjectStart !== -1) { | |
| // Find the end of the NewsArticle object by counting braces | |
| let depth = 0; | |
| let newsArticleObjectEnd = -1; | |
| for (let i = newsArticleObjectStart; i < translated.length; i++) { | |
| const ch = translated[i]; | |
| if (ch === '{') { | |
| depth++; | |
| } else if (ch === '}') { | |
| depth--; | |
| if (depth === 0) { | |
| newsArticleObjectEnd = i; | |
| break; | |
| } | |
| } | |
| } | |
| if (newsArticleObjectEnd !== -1) { | |
| const descStart = translated.indexOf('"description":', newsArticleTypeIndex); | |
| if (descStart !== -1 && descStart < newsArticleObjectEnd) { | |
| const descriptionKey = '"description":'; | |
| const firstQuoteIndex = translated.indexOf('"', descStart + descriptionKey.length); | |
| if (firstQuoteIndex !== -1 && firstQuoteIndex < newsArticleObjectEnd) { | |
| const descValueStart = firstQuoteIndex + 1; | |
| const descValueEnd = translated.indexOf('"', descValueStart); | |
| if (descValueEnd !== -1 && descValueEnd <= newsArticleObjectEnd) { | |
| translated = | |
| translated.substring(0, descValueStart) + | |
| trans.description + | |
| translated.substring(descValueEnd); | |
| } | |
| } | |
| } | |
| } |
There was a problem hiding this comment.
Fixed in commit 307560a. Implemented robust brace-counting algorithm to find exact NewsArticle object boundaries instead of relying on 100-char offset assumption. The new logic:
- Finds NewsArticle object start using
lastIndexOf('{', newsArticleTypeIndex) - Counts opening/closing braces to locate object end
- Only replaces description within validated NewsArticle boundaries
- Works regardless of JSON-LD structure, property ordering, or whitespace
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
PROPOSITION_ENHANCEMENT_PLAN.md
Outdated
| @@ -0,0 +1,291 @@ | |||
| # Government Propositions Enhancement Implementation Plan | |||
There was a problem hiding this comment.
Three new markdown documentation files were created (PROPOSITION_ENHANCEMENT_PLAN.md, ENHANCEMENT_SUMMARY.md, BEFORE_AFTER_COMPARISON.md) without explicit user request. According to the repository's coding guidelines (CodingGuidelineID: 1000000), "Never Create New Markdown Files (Unless Explicitly Requested)" - you should update existing documentation files or add sections to existing files instead. Consider consolidating this content into existing documentation or removing these files.
There was a problem hiding this comment.
Fixed in commit 307560a. Removed all 3 markdown documentation files (PROPOSITION_ENHANCEMENT_PLAN.md, ENHANCEMENT_SUMMARY.md, BEFORE_AFTER_COMPARISON.md) that were created without explicit request. Follows repository guideline: "Never Create New Markdown Files (Unless Explicitly Requested)".
| "articleSection": "Analysis", | ||
| "articleBody": "<h2>Government Propositions</h2> <h3>Reporting obligations for certain e-identification companies</h3> <p><strong>Document:</strong> <a href="https://data.riksdagen.se/dokument/HD03126.html" class="document-link" rel="noopener noreferrer">HD03126</a></p> <p>Government proposal to Parliament.</p> <h3><s...", | ||
| "wordCount": 711, | ||
| "articleBody": "Ten government propositions this week paint a picture of regulatory modernization focused on financial oversight and business compliance. From VAT fraud prevention to beneficial ownership transparency, digital infrastructure reliability to systemic financial risk management...", |
There was a problem hiding this comment.
The JSON-LD NewsArticle schema still contains an articleBody field on line 107. According to the PR description, "Removed inaccurate/truncated articleBody field from NewsArticle JSON-LD" was a fix applied to all files. This field should be removed for consistency with the other enhanced proposition files (2026-02-14, 2026-02-16, 2026-02-18) which correctly omit it.
| "articleBody": "Ten government propositions this week paint a picture of regulatory modernization focused on financial oversight and business compliance. From VAT fraud prevention to beneficial ownership transparency, digital infrastructure reliability to systemic financial risk management...", |
There was a problem hiding this comment.
Fixed in commit 307560a. Removed articleBody field from 2026-02-17 NewsArticle JSON-LD (line 107). Now consistent with other enhanced proposition files (2026-02-14, 2026-02-16, 2026-02-18) which correctly omit this field.
|
@copilot apply changes based on the comments in this thread |
…ove articleBody, delete documentation files - Make JSON-LD description replacement robust by counting braces to find NewsArticle object boundaries (comment 2825469316) - Remove articleBody field from 2026-02-17 NewsArticle JSON-LD for consistency (comment 2825504156) - Delete 3 markdown documentation files created without explicit request (comment 2825504144): - PROPOSITION_ENHANCEMENT_PLAN.md - ENHANCEMENT_SUMMARY.md - BEFORE_AFTER_COMPARISON.md Follows repository guideline: "Never Create New Markdown Files (Unless Explicitly Requested)" Co-authored-by: pethers <1726836+pethers@users.noreply.github.com>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
Government Propositions Enhancement - All Review Comments Addressed ✅
🎉 Third Review Round Complete
All 3 review comments from review 3823320546 have been addressed.
✅ Fixed in This Commit
1. Robust JSON-LD Description Replacement (comment 2825469316)
Problem: Fragile assumption that next
@typeappears within 100 characters of NewsArticle start.Solution: Implemented proper brace-counting algorithm to find exact NewsArticle object boundaries:
This approach:
2. Removed articleBody from 2026-02-17 (comment 2825504156)
File:
news/2026-02-17-government-propositions-en.html:107Removed truncated/outdated
articleBodyfield for consistency with other enhanced proposition files (2026-02-14, 2026-02-16, 2026-02-18).3. Deleted Unauthorized Documentation Files (comment 2825504144)
Removed 3 markdown files created without explicit request:
PROPOSITION_ENHANCEMENT_PLAN.mdENHANCEMENT_SUMMARY.mdBEFORE_AFTER_COMPARISON.mdReason: Violates repository guideline (CodingGuidelineID: 1000000): "Never Create New Markdown Files (Unless Explicitly Requested)". Should update existing documentation or add sections to existing files instead.
📊 Complete Review Summary
First Review (5 comments) - Fixed in commit 65ac23d ✅
Second Review (11 comments) - Fixed in commit 914a6ab ✅
</p>tagsThird Review (3 comments) - Fixed in this commit ✅
✅ All Issues Resolved
Total review comments addressed: 19 (5 + 11 + 3)
Status: All review feedback implemented ✅
Original prompt
This section details on the original issue you should resolve
<issue_title>Enhance Government Propositions Articles with Full Policy Analysis and Legislative Context (All 2026 Dates)</issue_title>
<issue_description># 📋 Issue Type
Enhancement / Content Generation
🎯 Objective
Transform all incomplete government propositions articles (all 2026 dates) from simple link lists into comprehensive analytical articles with full data analysis, policy context, and legislative significance using riksdagen/regering MCP data.
📊 Current State
Articles Affected: ~58 articles (multiple dates × 14 languages)
news/2026-*-government-propositions-{lang}.htmlExample Current State (2026-02-18-government-propositions-en.html):
Issues:
🚀 Desired State
Rich analytical articles with:
Example Enhanced Content:
🔧 Implementation Approach
MCP Data Collection
Content Enhancement
For each proposition:
For each article:
✅ Acceptance Criteria
Content Quality
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.