Skip to content

SONARHTML-384 Skip non-HTML ERB files#675

Draft
erwan-leforestier-sonarsource wants to merge 3 commits into
masterfrom
SONARHTML-384-narrow-erb-selection
Draft

SONARHTML-384 Skip non-HTML ERB files#675
erwan-leforestier-sonarsource wants to merge 3 commits into
masterfrom
SONARHTML-384-narrow-erb-selection

Conversation

@erwan-leforestier-sonarsource
Copy link
Copy Markdown
Contributor

@erwan-leforestier-sonarsource erwan-leforestier-sonarsource commented May 27, 2026

Summary

Narrow .erb file selection in sonar-html: an ERB template is only analyzed when its intermediate extension (before .erb) is one sonar-html already recognizes — i.e. it belongs to one of the existing layers (HTML language suffixes, JSP language suffixes, or the hard-coded OTHER_FILE_SUFFIXES in HtmlSensor). This drops Dockerfile.erb, config.yml.erb, app.js.erb, etc. without introducing a per-filename blocklist.

Changes

  • ErbFileFilter.shouldSkip(filename, recognizedExtensions) — decides whether a .erb file lacks a recognized double extension and should be skipped.
  • HtmlSensor builds the recognized set from sonar.html.file.suffixes + sonar.jsp.file.suffixes + OTHER_FILE_SUFFIXES (with the constants' defaults as a floor) and skips files flagged by the filter at debug level.
  • Unit tests for the filter and integration tests on HtmlSensor (skip Dockerfile.erb, config.yml.erb, index.erb; analyze page.html.erb, script.php.erb).

Functional Validation

Attached: SONARHTML-384-fv.zip

Unzip and run:
./run.sh

Expected output is in expected-output.txt. The README shows the
before/after comparison so you can reproduce the difference directly.

⚠️⚠️ This is not ready for review ⚠️⚠️

@hashicorp-vault-sonar-prod
Copy link
Copy Markdown

hashicorp-vault-sonar-prod Bot commented May 27, 2026

SONARHTML-384

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 27, 2026

Ruling Report

The following ruling changes are in this PR:

Rule: Web-BoldAndItalicTagsCheck

🔽 Code no longer flagged (5 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:17

    12 | 
    13 |   request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
    14 | %>
    15 | 
    16 | <h2 style="margin-top: 30px">Request</h2>
>   17 | <p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
    18 | 
    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:24

    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 
    23 | <h2 style="margin-top: 30px">Response</h2>
>   24 | <p><b>Headers</b>: <pre><%=h response ? response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/template_error.erb:7

     2 |   <%=h @exception.original_exception.class.to_s %> in
     3 |   <%=h request.parameters["controller"].capitalize if request.parameters["controller"]%>#<%=h request.parameters["action"] %>
     4 | </h1>
     5 | 
     6 | <p>
>    7 |   Showing <i><%=h @exception.file_name %></i> where line <b>#<%=h @exception.line_number %></b> raised:
     8 |   <pre><code><%=h @exception.message %></code></pre>
     9 | </p>
    10 | 
    11 | <p>Extracted source (around line <b>#<%=h @exception.line_number %></b>):
    12 | <pre><code><%=h @exception.source_extract %></code></pre></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/template_error.erb:7

     2 |   <%=h @exception.original_exception.class.to_s %> in
     3 |   <%=h request.parameters["controller"].capitalize if request.parameters["controller"]%>#<%=h request.parameters["action"] %>
     4 | </h1>
     5 | 
     6 | <p>
>    7 |   Showing <i><%=h @exception.file_name %></i> where line <b>#<%=h @exception.line_number %></b> raised:
     8 |   <pre><code><%=h @exception.message %></code></pre>
     9 | </p>
    10 | 
    11 | <p>Extracted source (around line <b>#<%=h @exception.line_number %></b>):
    12 | <pre><code><%=h @exception.source_extract %></code></pre></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/template_error.erb:11

     6 | <p>
     7 |   Showing <i><%=h @exception.file_name %></i> where line <b>#<%=h @exception.line_number %></b> raised:
     8 |   <pre><code><%=h @exception.message %></code></pre>
     9 | </p>
    10 | 
>   11 | <p>Extracted source (around line <b>#<%=h @exception.line_number %></b>):
    12 | <pre><code><%=h @exception.source_extract %></code></pre></p>
    13 | 
    14 | <p><%=h @exception.sub_template_message %></p>
    15 | 
    16 | <% @real_exception = @exception

Rule: Web-DoctypePresenceCheck

🔽 Code no longer flagged (1 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/layout.erb:1

>    1 | <html xmlns="http://www.w3.org/1999/xhtml">
     2 | <head>
     3 |   <title>Action Controller: Exception caught</title>
     4 |   <style>
     5 |     body { background-color: #fff; color: #333; }
     6 | 

Rule: Web-HeaderCheck

🔽 Code no longer flagged (14 issues)

sonar-master/sonar-plugin-api/src/test/resources/org/sonar/api/web/AbstractRubyTemplateTest/template.erb:0

     1 | ok

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/plugins/rails_page.erb:0

     1 | <%= render :inline => @page.getTemplate() %>

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:0

     1 | <%
     2 |    current_display_id = "sources_#{rand(100)}"
     3 | %>
     4 | 
     5 | <table id="<%= current_display_id %>" class="sources2 code" cellpadding="0" cellspacing="0">

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_line_numbers.erb:0

     1 | <td class="lid L<%= index -%>"><a name="L<%= index -%>" href="#L<%= index -%>"><%= index -%></a></td>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:0

     1 | <% unless @exception.blamed_files.blank? %>
     2 |   <% if (hide = @exception.blamed_files.length > 8) %>
     3 |     <a href="#" onclick="document.getElementById('blame_trace').style.display='block'; return false;">Show blamed files</a>
     4 |   <% end %>
     5 |   <pre id="blame_trace" <%='style="display:none"' if hide %>><code><%=h @exception.describe_blame %></code></pre>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_trace.erb:0

     1 | <%
     2 |   traces = [
     3 |     ["Application Trace", @exception.application_backtrace],
     4 |     ["Framework Trace", @exception.framework_backtrace],
     5 |     ["Full Trace", @exception.clean_backtrace]

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/diagnostics.erb:0

     1 | <h1>
     2 |   <%=h @exception.class.to_s %>
     3 |   <% if request.parameters['controller'] %>
     4 |     in <%=h request.parameters['controller'].humanize %>Controller<% if request.parameters['action'] %>#<%=h request.parameters['action'] %><% end %>
     5 |   <% end %>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/layout.erb:0

     1 | <html xmlns="http://www.w3.org/1999/xhtml">
     2 | <head>
     3 |   <title>Action Controller: Exception caught</title>
     4 |   <style>
     5 |     body { background-color: #fff; color: #333; }

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/missing_template.erb:0

     1 | <h1>Template is missing</h1>
     2 | <p><%=h @exception.message %></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/routing_error.erb:0

     1 | <h1>Routing Error</h1>
     2 | <p><pre><%=h @exception.message %></pre></p>
     3 | <% unless @exception.failures.empty? %><p>
     4 |   <h2>Failure reasons:</h2>
     5 |   <ol>

...and 4 more (see ruling JSON files for full list)

Rule: Web-InlineStyleCheck

🔽 Code no longer flagged (5 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:122

   117 | 
   118 |     <% end %>
   119 | </table>
   120 | 
   121 | <% if filtered && !has_displayed_lines %>
>  122 |   <p style="padding: 10px"><%= message('no_lines_match_your_filter_criteria') -%></p>
   123 | <% end %>
   124 | 
   125 | 
   126 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:16

    11 |   clean_params.delete("controller")
    12 | 
    13 |   request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
    14 | %>
    15 | 
>   16 | <h2 style="margin-top: 30px">Request</h2>
    17 | <p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
    18 | 
    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:20

    15 | 
    16 | <h2 style="margin-top: 30px">Request</h2>
    17 | <p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
    18 | 
    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
>   20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 
    23 | <h2 style="margin-top: 30px">Response</h2>
    24 | <p><b>Headers</b>: <pre><%=h response ? response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:23

    18 | 
    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 
>   23 | <h2 style="margin-top: 30px">Response</h2>
    24 | <p><b>Headers</b>: <pre><%=h response ? response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_trace.erb:22

    17 |     %>
    18 |     <a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
    19 |   <% end %>
    20 | 
    21 |   <% traces.each do |name, trace| %>
>   22 |     <div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;">
    23 |       <pre><code><%=h trace.join "\n" %></code></pre>
    24 |     </div>
    25 |   <% end %>
    26 | </div>

Rule: Web-InternationalizationCheck

🔽 Code no longer flagged (6 issues)

sonar-master/sonar-plugin-api/src/test/resources/org/sonar/api/web/AbstractRubyTemplateTest/template.erb:1

>    1 | ok

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:16

    11 |   clean_params.delete("controller")
    12 | 
    13 |   request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
    14 | %>
    15 | 
>   16 | <h2 style="margin-top: 30px">Request</h2>
    17 | <p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
    18 | 
    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:17

    12 | 
    13 |   request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
    14 | %>
    15 | 
    16 | <h2 style="margin-top: 30px">Request</h2>
>   17 | <p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
    18 | 
    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:23

    18 | 
    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 
>   23 | <h2 style="margin-top: 30px">Response</h2>
    24 | <p><b>Headers</b>: <pre><%=h response ? response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:24

    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 
    23 | <h2 style="margin-top: 30px">Response</h2>
>   24 | <p><b>Headers</b>: <pre><%=h response ? response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/diagnostics.erb:4

     1 | <h1>
     2 |   <%=h @exception.class.to_s %>
     3 |   <% if request.parameters['controller'] %>
>    4 |     in <%=h request.parameters['controller'].humanize %>Controller<% if request.parameters['action'] %>#<%=h request.parameters['action'] %><% end %>
     5 |   <% end %>
     6 | </h1>
     7 | <pre><%=h @exception.clean_message %></pre>
     8 | 
     9 | <%= render :file => @rescues_path["rescues/_trace.erb"] %>

Rule: Web-JspScriptletCheck

🔽 Code no longer flagged (84 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/plugins/rails_page.erb:1

>    1 | <%= render :inline => @page.getTemplate() %>

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:1

>    1 | <%
     2 |    current_display_id = "sources_#{rand(100)}"
     3 | %>
     4 | 
     5 | <table id="<%= current_display_id %>" class="sources2 code" cellpadding="0" cellspacing="0">
     6 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:11

     6 | 
     7 |     <script>
     8 |       $j("#<%= current_display_id %>").on("click", "span.sym", {id: "<%= current_display_id.to_s() %>"}, highlight_usages);
     9 |     </script>
    10 | 
>   11 |         <%
    12 |            colspan=2
    13 |            gray_colspan=1
    14 |            white_colspan=0
    15 |            if display_manual_violation_form
    16 |              colspan+=1

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:43

    38 |              if previous_hidden && !first_section
    39 |                current_revision=nil
    40 | 
    41 |            %>
    42 | 
>   43 |         <%= render :partial => "shared/source_new_section", :locals => {:colspan => colspan} %>
    44 |       <%
    45 |          end
    46 |          previous_hidden=false
    47 |          first_section=false
    48 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:44

    39 |                current_revision=nil
    40 | 
    41 |            %>
    42 | 
    43 |         <%= render :partial => "shared/source_new_section", :locals => {:colspan => colspan} %>
>   44 |       <%
    45 |          end
    46 |          previous_hidden=false
    47 |          first_section=false
    48 | 
    49 |          status=hits_status=conditions_status=''

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:78

    73 |          end
    74 |       %>
    75 | 
    76 |       <tr class="row pos<%= index+1 -%>">
    77 | 
>   78 |           <% if display_manual_violation_form %>
    79 |             <%= render :partial => "shared/source_issue_form", :locals => { :resource_id => resource.id, \
    80 |                                                                                 :index => index, \
    81 |                                                                                 :gray_colspan => gray_colspan, \
    82 |                                                                                 :white_colspan => white_colspan} %>
    83 |           <% end %>

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:79

    74 |       %>
    75 | 
    76 |       <tr class="row pos<%= index+1 -%>">
    77 | 
    78 |           <% if display_manual_violation_form %>
>   79 |             <%= render :partial => "shared/source_issue_form", :locals => { :resource_id => resource.id, \
    80 |                                                                                 :index => index, \
    81 |                                                                                 :gray_colspan => gray_colspan, \
    82 |                                                                                 :white_colspan => white_colspan} %>
    83 |           <% end %>
    84 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:83

    78 |           <% if display_manual_violation_form %>
    79 |             <%= render :partial => "shared/source_issue_form", :locals => { :resource_id => resource.id, \
    80 |                                                                                 :index => index, \
    81 |                                                                                 :gray_colspan => gray_colspan, \
    82 |                                                                                 :white_colspan => white_colspan} %>
>   83 |           <% end %>
    84 | 
    85 |           <% if scm_available
    86 |                if current_revision!=line.revision
    87 |                  current_revision=line.revision
    88 |                  title = "Revision #{h(line.revision)}"

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:85

    80 |                                                                                 :index => index, \
    81 |                                                                                 :gray_colspan => gray_colspan, \
    82 |                                                                                 :white_colspan => white_colspan} %>
    83 |           <% end %>
    84 | 
>   85 |           <% if scm_available
    86 |                if current_revision!=line.revision
    87 |                  current_revision=line.revision
    88 |                  title = "Revision #{h(line.revision)}"
    89 |           %>
    90 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:91

    86 |                if current_revision!=line.revision
    87 |                  current_revision=line.revision
    88 |                  title = "Revision #{h(line.revision)}"
    89 |           %>
    90 | 
>   91 |               <%= render :partial => "shared/source_scm", :locals => {:line => line, :title => title} %>
    92 | 
    93 |             <% else %>
    94 |               <td class="scm"></td>
    95 |             <% end %>
    96 | 

...and 74 more (see ruling JSON files for full list)

Rule: Web-LinkToNothingCheck

🔽 Code no longer flagged (3 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:3

     1 | <% unless @exception.blamed_files.blank? %>
     2 |   <% if (hide = @exception.blamed_files.length > 8) %>
>    3 |     <a href="#" onclick="document.getElementById('blame_trace').style.display='block'; return false;">Show blamed files</a>
     4 |   <% end %>
     5 |   <pre id="blame_trace" <%='style="display:none"' if hide %>><code><%=h @exception.describe_blame %></code></pre>
     6 | <% end %>
     7 | 
     8 | <%

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:19

    14 | %>
    15 | 
    16 | <h2 style="margin-top: 30px">Request</h2>
    17 | <p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
    18 | 
>   19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 
    23 | <h2 style="margin-top: 30px">Response</h2>
    24 | <p><b>Headers</b>: <pre><%=h response ? response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_trace.erb:18

    13 |   <% names.each do |name| %>
    14 |     <%
    15 |       show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';"
    16 |       hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"}
    17 |     %>
>   18 |     <a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
    19 |   <% end %>
    20 | 
    21 |   <% traces.each do |name, trace| %>
    22 |     <div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;">
    23 |       <pre><code><%=h trace.join "\n" %></code></pre>

Rule: Web-MaxLineLengthCheck

🔽 Code no longer flagged (9 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:8

     3 | %>
     4 | 
     5 | <table id="<%= current_display_id %>" class="sources2 code" cellpadding="0" cellspacing="0">
     6 | 
     7 |     <script>
>    8 |       $j("#<%= current_display_id %>").on("click", "span.sym", {id: "<%= current_display_id.to_s() %>"}, highlight_usages);
     9 |     </script>
    10 | 
    11 |         <%
    12 |            colspan=2
    13 |            gray_colspan=1

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:103

    98 | 
    99 |           <%= render :partial => "shared/source_line_numbers", :locals => {:index => line.id} %>
   100 | 
   101 |           <% if display_coverage %>
   102 |             <%= render :partial => "shared/source_coverage", :locals => {:line => line, \
>  103 |                                                                           :statuses => {:hits => hits_status, :conditions => conditions_status}, \
   104 |                                                                           :index => index, \
   105 |                                                                           :resource_key => snapshot.resource.key} %>
   106 |           <% end %>
   107 | 
   108 |           <%= render :partial => "shared/source_code", :locals => {:code => line.source, :status => status} %>

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:114

   109 | 
   110 |       </tr>
   111 | 
   112 |       <% if display_issues && line.issues? %>
   113 |         <%= render :partial => "shared/source_issues", :locals => { :line => line, \
>  114 |                                                                     :display_manual_violation_form => display_manual_violation_form, \
   115 |                                                                     :scm_available => scm_available} %>
   116 |       <% end %>
   117 | 
   118 |     <% end %>
   119 | </table>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:3

     1 | <% unless @exception.blamed_files.blank? %>
     2 |   <% if (hide = @exception.blamed_files.length > 8) %>
>    3 |     <a href="#" onclick="document.getElementById('blame_trace').style.display='block'; return false;">Show blamed files</a>
     4 |   <% end %>
     5 |   <pre id="blame_trace" <%='style="display:none"' if hide %>><code><%=h @exception.describe_blame %></code></pre>
     6 | <% end %>
     7 | 
     8 | <%

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:19

    14 | %>
    15 | 
    16 | <h2 style="margin-top: 30px">Request</h2>
    17 | <p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
    18 | 
>   19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 
    23 | <h2 style="margin-top: 30px">Response</h2>
    24 | <p><b>Headers</b>: <pre><%=h response ? response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_trace.erb:16

    11 | 
    12 | <div id="traces">
    13 |   <% names.each do |name| %>
    14 |     <%
    15 |       show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';"
>   16 |       hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"}
    17 |     %>
    18 |     <a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
    19 |   <% end %>
    20 | 
    21 |   <% traces.each do |name, trace| %>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_trace.erb:18

    13 |   <% names.each do |name| %>
    14 |     <%
    15 |       show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';"
    16 |       hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"}
    17 |     %>
>   18 |     <a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
    19 |   <% end %>
    20 | 
    21 |   <% traces.each do |name, trace| %>
    22 |     <div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;">
    23 |       <pre><code><%=h trace.join "\n" %></code></pre>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/diagnostics.erb:4

     1 | <h1>
     2 |   <%=h @exception.class.to_s %>
     3 |   <% if request.parameters['controller'] %>
>    4 |     in <%=h request.parameters['controller'].humanize %>Controller<% if request.parameters['action'] %>#<%=h request.parameters['action'] %><% end %>
     5 |   <% end %>
     6 | </h1>
     7 | <pre><%=h @exception.clean_message %></pre>
     8 | 
     9 | <%= render :file => @rescues_path["rescues/_trace.erb"] %>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/template_error.erb:3

     1 | <h1>
     2 |   <%=h @exception.original_exception.class.to_s %> in
>    3 |   <%=h request.parameters["controller"].capitalize if request.parameters["controller"]%>#<%=h request.parameters["action"] %>
     4 | </h1>
     5 | 
     6 | <p>
     7 |   Showing <i><%=h @exception.file_name %></i> where line <b>#<%=h @exception.line_number %></b> raised:
     8 |   <pre><code><%=h @exception.message %></code></pre>

Rule: Web-NonConsecutiveHeadingCheck

🔽 Code no longer flagged (1 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:16

    11 |   clean_params.delete("controller")
    12 | 
    13 |   request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
    14 | %>
    15 | 
>   16 | <h2 style="margin-top: 30px">Request</h2>
    17 | <p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
    18 | 
    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 

Rule: Web-PageWithoutFaviconCheck

🔽 Code no longer flagged (1 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/layout.erb:2

     1 | <html xmlns="http://www.w3.org/1999/xhtml">
>    2 | <head>
     3 |   <title>Action Controller: Exception caught</title>
     4 |   <style>
     5 |     body { background-color: #fff; color: #333; }
     6 | 
     7 |     body, p, ol, ul, td {

Rule: Web-S1827

🔽 Code no longer flagged (3 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:5

     1 | <%
     2 |    current_display_id = "sources_#{rand(100)}"
     3 | %>
     4 | 
>    5 | <table id="<%= current_display_id %>" class="sources2 code" cellpadding="0" cellspacing="0">
     6 | 
     7 |     <script>
     8 |       $j("#<%= current_display_id %>").on("click", "span.sym", {id: "<%= current_display_id.to_s() %>"}, highlight_usages);
     9 |     </script>
    10 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:5

     1 | <%
     2 |    current_display_id = "sources_#{rand(100)}"
     3 | %>
     4 | 
>    5 | <table id="<%= current_display_id %>" class="sources2 code" cellpadding="0" cellspacing="0">
     6 | 
     7 |     <script>
     8 |       $j("#<%= current_display_id %>").on("click", "span.sym", {id: "<%= current_display_id.to_s() %>"}, highlight_usages);
     9 |     </script>
    10 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_line_numbers.erb:1

>    1 | <td class="lid L<%= index -%>"><a name="L<%= index -%>" href="#L<%= index -%>"><%= index -%></a></td>

Rule: Web-S5254

🔽 Code no longer flagged (1 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/layout.erb:1

>    1 | <html xmlns="http://www.w3.org/1999/xhtml">
     2 | <head>
     3 |   <title>Action Controller: Exception caught</title>
     4 |   <style>
     5 |     body { background-color: #fff; color: #333; }
     6 | 

Rule: Web-S5256

🔽 Code no longer flagged (1 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:5

     1 | <%
     2 |    current_display_id = "sources_#{rand(100)}"
     3 | %>
     4 | 
>    5 | <table id="<%= current_display_id %>" class="sources2 code" cellpadding="0" cellspacing="0">
     6 | 
     7 |     <script>
     8 |       $j("#<%= current_display_id %>").on("click", "span.sym", {id: "<%= current_display_id.to_s() %>"}, highlight_usages);
     9 |     </script>
    10 | 

Rule: Web-S6844

🔽 Code no longer flagged (3 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:3

     1 | <% unless @exception.blamed_files.blank? %>
     2 |   <% if (hide = @exception.blamed_files.length > 8) %>
>    3 |     <a href="#" onclick="document.getElementById('blame_trace').style.display='block'; return false;">Show blamed files</a>
     4 |   <% end %>
     5 |   <pre id="blame_trace" <%='style="display:none"' if hide %>><code><%=h @exception.describe_blame %></code></pre>
     6 | <% end %>
     7 | 
     8 | <%

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:19

    14 | %>
    15 | 
    16 | <h2 style="margin-top: 30px">Request</h2>
    17 | <p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
    18 | 
>   19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 
    23 | <h2 style="margin-top: 30px">Response</h2>
    24 | <p><b>Headers</b>: <pre><%=h response ? response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_trace.erb:18

    13 |   <% names.each do |name| %>
    14 |     <%
    15 |       show = "document.getElementById('#{name.gsub /\s/, '-'}').style.display='block';"
    16 |       hide = (names - [name]).collect {|hide_name| "document.getElementById('#{hide_name.gsub /\s/, '-'}').style.display='none';"}
    17 |     %>
>   18 |     <a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
    19 |   <% end %>
    20 | 
    21 |   <% traces.each do |name, trace| %>
    22 |     <div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;">
    23 |       <pre><code><%=h trace.join "\n" %></code></pre>

Rule: Web-TableWithoutCaptionCheck

🔽 Code no longer flagged (1 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:5

     1 | <%
     2 |    current_display_id = "sources_#{rand(100)}"
     3 | %>
     4 | 
>    5 | <table id="<%= current_display_id %>" class="sources2 code" cellpadding="0" cellspacing="0">
     6 | 
     7 |     <script>
     8 |       $j("#<%= current_display_id %>").on("click", "span.sym", {id: "<%= current_display_id.to_s() %>"}, highlight_usages);
     9 |     </script>
    10 | 

Rule: Web-Template_DoNotUseNameProperty

🔽 Code no longer flagged (1 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_line_numbers.erb:1

>    1 | <td class="lid L<%= index -%>"><a name="L<%= index -%>" href="#L<%= index -%>"><%= index -%></a></td>

Rule: Web-Template_RequiredScriptType

🔽 Code no longer flagged (1 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:7

     2 |    current_display_id = "sources_#{rand(100)}"
     3 | %>
     4 | 
     5 | <table id="<%= current_display_id %>" class="sources2 code" cellpadding="0" cellspacing="0">
     6 | 
>    7 |     <script>
     8 |       $j("#<%= current_display_id %>").on("click", "span.sym", {id: "<%= current_display_id.to_s() %>"}, highlight_usages);
     9 |     </script>
    10 | 
    11 |         <%
    12 |            colspan=2

Rule: Web-WhiteSpaceAroundCheck

🔽 Code no longer flagged (25 issues)

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_display.erb:122

   117 | 
   118 |     <% end %>
   119 | </table>
   120 | 
   121 | <% if filtered && !has_displayed_lines %>
>  122 |   <p style="padding: 10px"><%= message('no_lines_match_your_filter_criteria') -%></p>
   123 | <% end %>
   124 | 
   125 | 
   126 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/app/views/shared/_source_line_numbers.erb:1

>    1 | <td class="lid L<%= index -%>"><a name="L<%= index -%>" href="#L<%= index -%>"><%= index -%></a></td>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:5

     1 | <% unless @exception.blamed_files.blank? %>
     2 |   <% if (hide = @exception.blamed_files.length > 8) %>
     3 |     <a href="#" onclick="document.getElementById('blame_trace').style.display='block'; return false;">Show blamed files</a>
     4 |   <% end %>
>    5 |   <pre id="blame_trace" <%='style="display:none"' if hide %>><code><%=h @exception.describe_blame %></code></pre>
     6 | <% end %>
     7 | 
     8 | <%
     9 |   clean_params = request.parameters.clone
    10 |   clean_params.delete("action")

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:17

    12 | 
    13 |   request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
    14 | %>
    15 | 
    16 | <h2 style="margin-top: 30px">Request</h2>
>   17 | <p><b>Parameters</b>: <pre><%=h request_dump %></pre></p>
    18 | 
    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_request_and_response.erb:24

    19 | <p><a href="#" onclick="document.getElementById('session_dump').style.display='block'; return false;">Show session dump</a></p>
    20 | <div id="session_dump" style="display:none"><%= debug(request.session.instance_variable_get("@data")) %></div>
    21 | 
    22 | 
    23 | <h2 style="margin-top: 30px">Response</h2>
>   24 | <p><b>Headers</b>: <pre><%=h response ? response.headers.inspect.gsub(',', ",\n") : 'None' %></pre></p>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/_trace.erb:23

    18 |     <a href="#" onclick="<%= hide.join %><%= show %>; return false;"><%= name %></a> <%= '|' unless names.last == name %>
    19 |   <% end %>
    20 | 
    21 |   <% traces.each do |name, trace| %>
    22 |     <div id="<%= name.gsub /\s/, '-' %>" style="display: <%= name == "Application Trace" ? 'block' : 'none' %>;">
>   23 |       <pre><code><%=h trace.join "\n" %></code></pre>
    24 |     </div>
    25 |   <% end %>
    26 | </div>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/diagnostics.erb:2

     1 | <h1>
>    2 |   <%=h @exception.class.to_s %>
     3 |   <% if request.parameters['controller'] %>
     4 |     in <%=h request.parameters['controller'].humanize %>Controller<% if request.parameters['action'] %>#<%=h request.parameters['action'] %><% end %>
     5 |   <% end %>
     6 | </h1>
     7 | <pre><%=h @exception.clean_message %></pre>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/diagnostics.erb:4

     1 | <h1>
     2 |   <%=h @exception.class.to_s %>
     3 |   <% if request.parameters['controller'] %>
>    4 |     in <%=h request.parameters['controller'].humanize %>Controller<% if request.parameters['action'] %>#<%=h request.parameters['action'] %><% end %>
     5 |   <% end %>
     6 | </h1>
     7 | <pre><%=h @exception.clean_message %></pre>
     8 | 
     9 | <%= render :file => @rescues_path["rescues/_trace.erb"] %>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/diagnostics.erb:4

     1 | <h1>
     2 |   <%=h @exception.class.to_s %>
     3 |   <% if request.parameters['controller'] %>
>    4 |     in <%=h request.parameters['controller'].humanize %>Controller<% if request.parameters['action'] %>#<%=h request.parameters['action'] %><% end %>
     5 |   <% end %>
     6 | </h1>
     7 | <pre><%=h @exception.clean_message %></pre>
     8 | 
     9 | <%= render :file => @rescues_path["rescues/_trace.erb"] %>

sonar-master/sonar-server/src/main/webapp/WEB-INF/gems/gems/actionpack-2.3.15/lib/action_controller/templates/rescues/diagnostics.erb:7

     2 |   <%=h @exception.class.to_s %>
     3 |   <% if request.parameters['controller'] %>
     4 |     in <%=h request.parameters['controller'].humanize %>Controller<% if request.parameters['action'] %>#<%=h request.parameters['action'] %><% end %>
     5 |   <% end %>
     6 | </h1>
>    7 | <pre><%=h @exception.clean_message %></pre>
     8 | 
     9 | <%= render :file => @rescues_path["rescues/_trace.erb"] %>
    10 | 
    11 | <%= render :file => @rescues_path["rescues/_request_and_response.erb"] %>

...and 15 more (see ruling JSON files for full list)


Ruling has been auto-updated. A verification build will run automatically.

@sonarqube-next
Copy link
Copy Markdown

}

@Test
void erb_with_recognized_non_html_intermediate_extension_should_be_analyzed() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Quality: Test name is misleading: says 'non_html' but tests recognized ext

The test erb_with_recognized_non_html_intermediate_extension_should_be_analyzed is confusingly named. It tests that script.php.erb IS analyzed because .php is a recognized extension. The word 'non_html' makes it sound like the file should NOT be analyzed, but the assertion confirms it should. A clearer name would reflect that this tests a recognized (non-strictly-HTML) intermediate extension being properly kept.

Remove 'non_html' from the name since PHP is recognized by sonar-html and the file should be analyzed:

@Test
void erb_with_recognized_intermediate_extension_should_be_analyzed() {
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

🤖 Generated with GitHub Actions
@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented May 27, 2026

Code Review 👍 Approved with suggestions 0 resolved / 1 findings

Narrows .erb file selection to exclude non-HTML Ruby templates, resolving spurious analysis errors. Rename the misleading 'non_html' test case to accurately reflect the validated file extension logic.

💡 Quality: Test name is misleading: says 'non_html' but tests recognized ext

📄 sonar-html-plugin/src/test/java/org/sonar/plugins/html/core/HtmlSensorTest.java:355

The test erb_with_recognized_non_html_intermediate_extension_should_be_analyzed is confusingly named. It tests that script.php.erb IS analyzed because .php is a recognized extension. The word 'non_html' makes it sound like the file should NOT be analyzed, but the assertion confirms it should. A clearer name would reflect that this tests a recognized (non-strictly-HTML) intermediate extension being properly kept.

Remove 'non_html' from the name since PHP is recognized by sonar-html and the file should be analyzed
@Test
void erb_with_recognized_intermediate_extension_should_be_analyzed() {
🤖 Prompt for agents
Code Review: Narrows .erb file selection to exclude non-HTML Ruby templates, resolving spurious analysis errors. Rename the misleading 'non_html' test case to accurately reflect the validated file extension logic.

1. 💡 Quality: Test name is misleading: says 'non_html' but tests recognized ext
   Files: sonar-html-plugin/src/test/java/org/sonar/plugins/html/core/HtmlSensorTest.java:355

   The test `erb_with_recognized_non_html_intermediate_extension_should_be_analyzed` is confusingly named. It tests that `script.php.erb` IS analyzed because `.php` is a recognized extension. The word 'non_html' makes it sound like the file should NOT be analyzed, but the assertion confirms it should. A clearer name would reflect that this tests a recognized (non-strictly-HTML) intermediate extension being properly kept.

   Fix (Remove 'non_html' from the name since PHP is recognized by sonar-html and the file should be analyzed):
   @Test
   void erb_with_recognized_intermediate_extension_should_be_analyzed() {

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@vdiez
Copy link
Copy Markdown
Contributor

vdiez commented May 28, 2026

I checked SONARHTML-384 before replying here. The ticket is intentionally framed as "investigate the best approach", and the only Jira comment also questions whether this filtering should live in sonar-html at all or be delegated to the scanner. So I do not think we should lock ourselves too quickly into the current whitelist rule.

Initially the target was to stop analyzing files like Dockerfile.erb. This does fix that, but it also skips all bare .erb files, which still feels like a filename-only guess. ERB is Ruby's general-purpose text templating format, not an HTML-specific one. With no extra context, I would only expect bare .erb to generate HTML in a minority of cases, maybe ~25-40%. But that prior changes a lot with context: a bare .erb under views/, templates/, or app/views/ is much more likely to be HTML. The ruling delta seems to show exactly that kind of file disappearing (.../app/views/.../*.erb, .../templates/rescues/*.erb), which looks like false negatives rather than just filtering out Dockerfile.erb.

So I think the limitation is that we are still purely guessing from the filename, whether that logic sits in sonar-html or in the scanner. A stronger and still very cheap approach would be a quick content sniff instead: read only the first few KB, strip ERB blocks (<% ... %>), and then apply a very small HTML heuristic on the remaining text.

Concretely, I mean something in this spirit:

String clean = firstChunk.replaceAll("<%[\\s\\S]*?%>", " ");
boolean strongHtml = Pattern.compile("<!DOCTYPE\\s+html\\b|<(?:html|head|body)\\b", Pattern.CASE_INSENSITIVE)
  .matcher(clean)
  .find();
long weakHtmlTags = Pattern.compile("<(?:div|span|p|a|ul|ol|li|table|tr|td|th|form|input|textarea|select|button|script|style|section|article|header|footer|nav|main)\\b", Pattern.CASE_INSENSITIVE)
  .matcher(clean)
  .results()
  .count();

boolean likelyHtml = strongHtml || weakHtmlTags >= 2;

The exact regex is not the important part; the point is that this is still computationally cheap: no parser, no full-file read required, just a couple of linear scans on the first 4-8 KB. It should also be more robust than the current extension whitelist because it would keep obvious HTML ERB templates even when they are named just foo.erb, while still rejecting things like Dockerfile.erb, config.yml.erb, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants