Module: StillActive::MarkdownHelper
Instance Method Summary collapse
- #alternatives_section(result) ⇒ Object
-
#language_ceiling_section(result) ⇒ Object
The language-runtime ceiling: a resolved version whose declared runtime constraint (Ruby
ruby_version, Pythonrequires_python) caps the runtime onto an EOL release (critical) or below the latest stable (note). - #markdown_table_body_line(gem_name:, data:) ⇒ Object
- #markdown_table_header_line ⇒ Object
-
#poison_rank(data) ⇒ Object
Ranks poison findings for the report: a cap that holds you BELOW THE FIX first (unpatchable without replacing the capper), then a merely security-relevant cap (vulnerable but patchable in place), then everything else.
-
#poison_section(result) ⇒ Object
A dormant dep that caps your tree below its latest major (the poison-pill signal).
-
#poison_security_marker(data) ⇒ Object
A prominent marker naming the known-vulnerable dependency a security-relevant cap pins you to.
- #ruby_line(ruby_info) ⇒ Object
-
#transitive_section(result) ⇒ Object
Flagged transitive gems can't be swapped directly; name the direct dependency that pulls each one in so the finding becomes actionable (#60).
Instance Method Details
#alternatives_section(result) ⇒ Object
90 91 92 93 94 95 96 97 98 99 |
# File 'lib/helpers/markdown_helper.rb', line 90 def alternatives_section(result) flagged = result.select do |_name, data| data[:alternatives] && !data[:alternatives].empty? end return "" if flagged.empty? lines = ["", "**Alternatives** (Ruby Toolbox leads, verify fit):"] flagged.each { |name, data| lines << "- #{MarkdownEscape.code_span(name)}: #{MarkdownEscape.inline(data[:alternatives].join(", "))}" } lines.join("\n") end |
#language_ceiling_section(result) ⇒ Object
The language-runtime ceiling: a resolved version whose declared runtime
constraint (Ruby ruby_version, Python requires_python) caps the runtime
onto an EOL release (critical) or below the latest stable (note). Ranked
worst-first with a tier label, mirroring the poison section.
174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/helpers/markdown_helper.rb', line 174 def language_ceiling_section(result) flagged = result.select { |_name, data| data[:language_ceiling] } return "" if flagged.empty? lines = ["", "**Runtime ceiling findings** (a resolved version capping the language runtime it can run on):"] ranked = flagged.sort_by { |name, data| [-ConstraintHelper::SEVERITY.index(data[:language_ceiling][:severity] || :note), name.to_s] } ranked.each do |name, data| ceiling = data[:language_ceiling] lines << "- **#{ceiling[:severity] || :note}** #{MarkdownEscape.code_span(name)} #{language_ceiling_receipt(ceiling, data)}" end lines.join("\n") end |
#markdown_table_body_line(gem_name:, data:) ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/helpers/markdown_helper.rb', line 36 def markdown_table_body_line(gem_name:, data:) repository_url = data[:repository_url] ruby_gems_url = data[:ruby_gems_url] inactive_repository_emoji = data[:last_activity_warning_emoji] using_latest_version_emoji = data[:up_to_date_emoji] formatted_name = markdown_url(text: gem_name, url: repository_url) formatted_version_used = if [:git, :path].include?(data[:source_type]) data[:version_used] ? "#{data[:version_used]} (#{data[:source_type]})" : "(#{data[:source_type]})" elsif data[:version_yanked] "#{data[:version_used]} (YANKED #{StillActive.config.critical_warning_emoji})" else version_with_date( text: data[:version_used], url: version_url(ruby_gems_url, data[:version_used]), date: data[:version_used_release_date], ) end formatted_latest_version = version_with_date( text: data[:latest_version], url: version_url(ruby_gems_url, data[:latest_version]), date: data[:latest_version_release_date], ) formatted_latest_pre_release = version_with_date( text: data[:latest_pre_release_version], url: version_url(ruby_gems_url, data[:latest_pre_release_version]), date: data[:latest_pre_release_version_release_date], ) formatted_last_commit = markdown_url(text: year_month(data[:last_commit_date]), url: repository_url) unsure = StillActive.config.unsure_emoji cells = [ inactive_repository_emoji || unsure, using_latest_version_emoji || unsure, format_scorecard(data[:scorecard_score]), format_vulns(data), formatted_name, formatted_version_used || unsure, formatted_latest_version || unsure, formatted_latest_pre_release || unsure, formatted_last_commit || unsure, format_libyear(data[:libyear]), format_license(data[:license]), ] "| #{cells.join(" | ")} |" end |
#markdown_table_header_line ⇒ Object
31 32 33 34 |
# File 'lib/helpers/markdown_helper.rb', line 31 def markdown_table_header_line "| activity | up to date? | OpenSSF | vulns | name | version used | latest version | latest pre-release | last commit | libyear | license |\n" \ "| -------- | ----------- | ------- | ----- | ---- | ------------ | -------------- | ------------------ | ----------- | ------- | ------- |" end |
#poison_rank(data) ⇒ Object
Ranks poison findings for the report: a cap that holds you BELOW THE FIX first (unpatchable without replacing the capper), then a merely security-relevant cap (vulnerable but patchable in place), then everything else.
146 147 148 149 150 151 |
# File 'lib/helpers/markdown_helper.rb', line 146 def poison_rank(data) return 0 if data[:poison_below_fix] return 1 if data[:poison_security_relevant] 2 end |
#poison_section(result) ⇒ Object
A dormant dep that caps your tree below its latest major (the poison-pill signal). Consistent with the other finding sections: worst caps + total, the direct parent named for a transitive pill. The full cap list is in the JSON.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/helpers/markdown_helper.rb', line 119 def poison_section(result) # Require constraints present, not just the poison flag, so an unexpected # empty set can't emit a dangling "- `gem` caps" bullet (symmetric with the # terminal's poison_line guard). flagged = result.select { |_name, data| data[:poison] && !Array(data[:constraints]).empty? } return "" if flagged.empty? lines = ["", "**Poison-pill findings** (dormant deps capping your tree below its latest major):"] # Worst-first so the act-now findings lead, then by magnitude for a stable, # triage-friendly order. ranked = flagged.sort_by do |name, data| [ poison_rank(data), # below-the-fix leads, then security-relevant, then the rest -ConstraintHelper::SEVERITY.index(data[:poison_severity] || :note), -Array(data[:constraints]).map { |c| c[:majors_behind].to_i }.max, name.to_s, ] end ranked.each do |name, data| lines << "- **#{data[:poison_severity] || :note}** #{poison_gem_ref(name, data)} caps #{poison_caps(data[:constraints])}#{poison_security_marker(data)}" end lines.join("\n") end |
#poison_security_marker(data) ⇒ Object
A prominent marker naming the known-vulnerable dependency a security-relevant cap pins you to. Leads with the stronger BELOW-THE-FIX claim (the CVE and its nearest fix, a version the cap forbids) when it applies, else the weaker "vulnerable but patchable" form.
157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/helpers/markdown_helper.rb', line 157 def poison_security_marker(data) return "" unless data[:poison_security_relevant] below = Array(data[:constraints]).select { |c| c[:capped_below_fix] } if below.any? receipts = below.map { |c| "#{c[:dependency]} below the fix (#{c[:below_fix_advisory]} fixed in #{c[:below_fix_fixed_in]})" }.uniq " ⚠ **pins #{receipts.join("; ")}**" else pinned = Array(data[:constraints]).select { |c| c[:capped_dep_vulnerable] }.map { |c| c[:dependency] }.uniq " ⚠ **pins vulnerable #{pinned.join(", ")}**" end end |
#ruby_line(ruby_info) ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# File 'lib/helpers/markdown_helper.rb', line 12 def ruby_line(ruby_info) version = ruby_info[:version] latest = ruby_info[:latest_version] libyear = ruby_info[:libyear] eol = ruby_info[:eol] eol_date = ruby_info[:eol_date] return "**Ruby #{version}** (latest) #{StillActive.config.success_emoji}" if version == latest libyear_part = libyear ? "#{libyear} libyears behind #{latest}" : "behind #{latest}" if eol eol_part = eol_date ? "EOL #{eol_date.strftime("%Y-%m-%d")}" : "EOL" "**Ruby #{version}** (#{eol_part}, #{libyear_part}) #{StillActive.config.critical_warning_emoji}" else "**Ruby #{version}** (#{libyear_part}) #{StillActive.config.warning_emoji}" end end |
#transitive_section(result) ⇒ Object
Flagged transitive gems can't be swapped directly; name the direct dependency that pulls each one in so the finding becomes actionable (#60).
103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/helpers/markdown_helper.rb', line 103 def transitive_section(result) flagged = result.select do |_name, data| data[:direct] == false && Array(data[:dependency_path]).length >= 2 && transitive_flagged?(data) end return "" if flagged.empty? lines = ["", "**Transitive findings** (pulled in by a direct dependency):"] flagged.each do |name, data| lines << "- #{MarkdownEscape.code_span(name)} via #{MarkdownEscape.code_span(data[:dependency_path].first)}" end lines.join("\n") end |