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
95 96 97 98 99 100 101 102 103 104 |
# File 'lib/helpers/markdown_helper.rb', line 95 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.
179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/helpers/markdown_helper.rb', line 179 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(DependencyHelper.identity(name, data))} #{language_ceiling_receipt(ceiling, data)}" end lines.join("\n") end |
#markdown_table_body_line(gem_name:, data:) ⇒ Object
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 89 90 91 92 93 |
# File 'lib/helpers/markdown_helper.rb', line 37 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: DependencyHelper.identity(gem_name, data), 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], ) # Only link when there is a date: markdown_url with a nil date + a present # repo returns "[](url)" (an invisible link, and truthy, so the `|| unsure` # fallback below never fires). Guard here so a missing date shows unsure. last_commit_ym = year_month(data[:last_commit_date]) formatted_last_commit = markdown_url(text: last_commit_ym, url: repository_url) if last_commit_ym 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
32 33 34 35 |
# File 'lib/helpers/markdown_helper.rb', line 32 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.
151 152 153 154 155 156 |
# File 'lib/helpers/markdown_helper.rb', line 151 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.
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/helpers/markdown_helper.rb', line 124 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.
162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/helpers/markdown_helper.rb', line 162 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
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/helpers/markdown_helper.rb', line 13 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).
108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/helpers/markdown_helper.rb', line 108 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 |