Fix: Uses unsafe JSON.load and injects unescaped external data into...#2650
Fix: Uses unsafe JSON.load and injects unescaped external data into...#2650shanecodezzz wants to merge 1 commit intofreeCodeCamp:mainfrom
Conversation
Replaced JSON.load with JSON.parse, added HTML escaping to all interpolated values, added nil-safe access guards, and wrapped HTTP/parse calls in error handling across every method.
paragon-review
left a comment
There was a problem hiding this comment.
Review Summary
This review identified 3 issues in 1 file.
Confidence score: 2/5
- High risk - 1 critical issue require immediate attention
Severity breakdown: Critical: 1, Medium: 2
1 file reviewed, 3 comments
Tip: @paragon-run <instructions> to chat with our agent or push fixes!
| if key == '__compat' | ||
| tmp = slug.split('/') | ||
| last_table_entry.add_child("<th><code>#{tmp.last}") | ||
| last_table_entry.add_child("<th><code>#{CGI.escapeHTML(tmp.last.to_s)}") |
There was a problem hiding this comment.
Bug: CGI
CGI.escapeHTML used 6 times but require 'cgi' is missing. Every compat table call raises NameError. Add require 'cgi' at the top of the file.
View Details
Location: lib/docs/filters/mdn/compat_tables.rb (lines 122)
Analysis
CGI.escapeHTML used 6 times but require 'cgi' is missing
| What fails | Every call to generate_compatibility_table_wrapper raises NameError: uninitialized constant CGI because require 'cgi' is never loaded in this file or its ancestors. |
| Result | NameError: uninitialized constant CGI — all MDN compat tables silently break |
| Expected | CGI module is loaded and escapeHTML works correctly for all 6 call sites |
| Impact | Complete breakage of MDN compatibility table generation. No compat tables render at all. |
How to reproduce
Run the doc scraper against any MDN page with a compat table, or: ruby -e "require './lib/docs/filters/mdn/compat_tables'; # triggers NameError on CGI.escapeHTML"Patch Details
- @json_data = parsed['data']
+ require 'cgi' unless defined?(CGI)
+ @json_data = parsed['data']AI Fix Prompt
Fix this issue: CGI.escapeHTML used 6 times but `require 'cgi'` is missing. Every compat table call raises NameError. Add `require 'cgi'` at the top of the file.
Location: lib/docs/filters/mdn/compat_tables.rb (lines 122)
Problem: Every call to generate_compatibility_table_wrapper raises NameError: uninitialized constant CGI because `require 'cgi'` is never loaded in this file or its ancestors.
Current behavior: NameError: uninitialized constant CGI — all MDN compat tables silently break
Expected: CGI module is loaded and escapeHTML works correctly for all 6 call sites
Steps to reproduce: Run the doc scraper against any MDN page with a compat table, or: ruby -e "require './lib/docs/filters/mdn/compat_tables'; # triggers NameError on CGI.escapeHTML"
Provide a code fix.
Tip: Reply with @paragon-run to automatically fix this issue
| end | ||
| version_removed.push(element['version_removed'] || false) | ||
| version_removed.push(element['version_removed'] ? CGI.escapeHTML(element['version_removed'].to_s) : false) | ||
| notes.push(element['notes'] || false) |
There was a problem hiding this comment.
Security: The notes field is interpolated into HTML unescaped
The notes field is interpolated into HTML unescaped. This is inconsistent with escaping applied elsewhere. Add a comment or sanitize notes.
View Details
Location: lib/docs/filters/mdn/compat_tables.rb (lines 170)
Analysis
The notes field is interpolated into HTML unescaped
| What fails | The notes field from BCD JSON is pushed raw (line 170) and interpolated directly into HTML (line 181) without CGI.escapeHTML, unlike all other fields in this PR. |
| Result | Raw HTML from the BCD API notes field is injected directly into output without sanitization |
| Expected | Either sanitize notes consistently with other fields, or document the intentional exception with a code comment explaining notes contains trusted HTML |
| Impact | XSS injection point if upstream BCD API is compromised or returns unexpected content. Inconsistent security posture within the same PR. |
How to reproduce
1. Examine line 170: notes.push(element['notes'] || false)
2. Examine line 181: "<details><summary>#{version_string}</summary>#{notes[value]}</details>"
3. Note that version, release_date, version_removed are all escaped but notes is not.AI Fix Prompt
Fix this issue: The `notes` field is interpolated into HTML unescaped. This is inconsistent with escaping applied elsewhere. Add a comment or sanitize notes.
Location: lib/docs/filters/mdn/compat_tables.rb (lines 170)
Problem: The `notes` field from BCD JSON is pushed raw (line 170) and interpolated directly into HTML (line 181) without CGI.escapeHTML, unlike all other fields in this PR.
Current behavior: Raw HTML from the BCD API notes field is injected directly into output without sanitization
Expected: Either sanitize notes consistently with other fields, or document the intentional exception with a code comment explaining notes contains trusted HTML
Steps to reproduce: 1. Examine line 170: notes.push(element['notes'] || false)
2. Examine line 181: "<details><summary>#{version_string}</summary>#{notes[value]}</details>"
3. Note that version, release_date, version_removed are all escaped but notes is not.
Provide a code fix.
Tip: Reply with @paragon-run to automatically fix this issue
| response = Request.run url | ||
| return "" unless response.success? | ||
| @json_data = JSON.load(response.body)['data'] | ||
| parsed = JSON.parse(response.body) |
There was a problem hiding this comment.
Bug: JSON
JSON.parse raises ParserError on malformed responses. This crashes the entire scraper. Wrap parse in rescue JSON::ParserError.
View Details
Location: lib/docs/filters/mdn/compat_tables.rb (lines 74)
Analysis
JSON.parse raises ParserError on malformed responses. This crashes the entire scraper
| What fails | JSON.parse(response.body) on line 74 raises JSON::ParserError if the BCD API returns a 200 with non-JSON body (maintenance page, HTML error page, etc.). |
| Result | Unhandled JSON::ParserError crashes the scraper for the entire doc set |
| Expected | Malformed JSON is caught gracefully with rescue JSON::ParserError and returns empty string, consistent with the existing error-handling pattern on line 73 |
| Impact | A transient upstream API issue crashes the entire doc generation pipeline instead of gracefully degrading one table. |
How to reproduce
1. Simulate a BCD API returning HTML on a 200 response
2. The scraper hits JSON.parse which throws JSON::ParserError
3. Entire doc generation crashes instead of skipping one tablePatch Details
- parsed = JSON.parse(response.body)
+ parsed = JSON.parse(response.body) rescue nil
+ return "" unless parsed.is_a?(Hash) && parsed['data'].is_a?(Hash)AI Fix Prompt
Fix this issue: JSON.parse raises ParserError on malformed responses. This crashes the entire scraper. Wrap parse in rescue JSON::ParserError.
Location: lib/docs/filters/mdn/compat_tables.rb (lines 74)
Problem: JSON.parse(response.body) on line 74 raises JSON::ParserError if the BCD API returns a 200 with non-JSON body (maintenance page, HTML error page, etc.).
Current behavior: Unhandled JSON::ParserError crashes the scraper for the entire doc set
Expected: Malformed JSON is caught gracefully with `rescue JSON::ParserError` and returns empty string, consistent with the existing error-handling pattern on line 73
Steps to reproduce: 1. Simulate a BCD API returning HTML on a 200 response
2. The scraper hits JSON.parse which throws JSON::ParserError
3. Entire doc generation crashes instead of skipping one table
Provide a code fix.
Tip: Reply with @paragon-run to automatically fix this issue
|
Hey @freeCodeCamp! We ran Paragon on this PR and it caught some real issues — check the inline comments above. Found 1 issues including bugs and security fixes. We know maintaining OSS is tough, especially without automated code review catching things before they hit prod. We set up open source projects with Paragon so you don't have to worry about that stuff. Shoot me an email at shane@polarity.so if you're interested, happy to get you set up! |
If you’re adding a new scraper, please ensure that you have:
public/icons/your_scraper_name/directory:16.png: a 16×16 pixel icon for the doc16@2x.png: a 32×32 pixel icon for the docSOURCE: A text file containing the URL to the page the image can be found on or the URL of the original image itselfIf you're updating existing documentation to its latest version, please ensure that you have:
SOURCEfile inpublic/icons/your_scraper_name/are up-to-date if the documentation has a custom iconself.linkscontains up-to-date urls ifself.linksis defined