neetob 0.5.68 → 0.5.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.env +2 -1
  3. data/.neetoci/default.yml +1 -1
  4. data/.ruby-version +1 -1
  5. data/Gemfile.lock +44 -21
  6. data/README.md +11 -0
  7. data/bookmarks.md +113 -113
  8. data/data/github-labels.json +80 -45
  9. data/data/repo-team-leads.json +82 -0
  10. data/exe/neetob +1 -1
  11. data/lib/neetob/cli/base.rb +67 -5
  12. data/lib/neetob/cli/cloudflare/automatic_https_rewrites.rb +34 -0
  13. data/lib/neetob/cli/cloudflare/base.rb +2 -2
  14. data/lib/neetob/cli/cloudflare/commands.rb +7 -0
  15. data/lib/neetob/cli/github/active_record_doctor.rb +1 -1
  16. data/lib/neetob/cli/github/brakeman.rb +1 -1
  17. data/lib/neetob/cli/github/bundle_audit.rb +1 -1
  18. data/lib/neetob/cli/github/issues/helpers.rb +40 -0
  19. data/lib/neetob/cli/github/make_pr/base.rb +1 -1
  20. data/lib/neetob/cli/github/repositories/pull_requests.rb +19 -0
  21. data/lib/neetob/cli/github/repositories/team_leads.rb +34 -0
  22. data/lib/neetob/cli/github/unused_assets_audit.rb +5 -1
  23. data/lib/neetob/cli/monthly_audit/commands.rb +2 -1
  24. data/lib/neetob/cli/monthly_audit/databases/users_unique_email_index.rb +6 -1
  25. data/lib/neetob/cli/monthly_audit/databases/uuid_primary_key.rb +8 -0
  26. data/lib/neetob/cli/monthly_audit/github_issue_creation.rb +75 -0
  27. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/always_use_https_is_enabled.rb +11 -0
  28. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/automatic_https_rewrites_is_enabled.rb +43 -0
  29. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/dns_entry_has_proxy_status.rb +9 -0
  30. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/main.rb +2 -2
  31. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/minimum_tls_version_is_one_point_two.rb +11 -0
  32. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/spf_records_are_valid.rb +9 -0
  33. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/ssl_tls_encryption_mode_set_to_full.rb +9 -0
  34. data/lib/neetob/cli/monthly_audit/instances_and_addons/cronitor/setup_correctly_for_apps.rb +10 -0
  35. data/lib/neetob/cli/monthly_audit/instances_and_addons/cronitor/setup_correctly_for_help_center.rb +12 -0
  36. data/lib/neetob/cli/monthly_audit/instances_and_addons/cronitor/setup_correctly_for_landing_pages.rb +15 -2
  37. data/lib/neetob/cli/monthly_audit/instances_and_addons/honeybadger/setup_correctly_for_apps.rb +28 -29
  38. data/lib/neetob/cli/monthly_audit/instances_and_addons/main.rb +5 -5
  39. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/cloudfront_cdn_enabled.rb +11 -17
  40. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/essential_environment_variables_set.rb +7 -10
  41. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/main.rb +0 -3
  42. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/scheduled_exports_enabled.rb +8 -4
  43. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/ssl_certificates_over_thirty_days_from_expiry.rb +69 -24
  44. data/lib/neetob/cli/monthly_audit/misc/main.rb +1 -1
  45. data/lib/neetob/cli/monthly_audit/misc/redirections_working_correctly.rb +14 -1
  46. data/lib/neetob/cli/monthly_audit/misc/sparkpost_sub_account_used_for_all_apps.rb +24 -18
  47. data/lib/neetob/cli/monthly_audit/perform.rb +7 -2
  48. data/lib/neetob/cli/monthly_audit/security/code/active_record_doctor.rb +10 -5
  49. data/lib/neetob/cli/monthly_audit/security/code/brakeman.rb +10 -2
  50. data/lib/neetob/cli/monthly_audit/security/code/bundle_audit.rb +19 -6
  51. data/lib/neetob/cli/monthly_audit/security/code/checks_for_unused_assets.rb +5 -0
  52. data/lib/neetob/cli/monthly_audit/security/code/fasterer.rb +10 -2
  53. data/lib/neetob/cli/monthly_audit/security/code/yarn_audit.rb +6 -1
  54. data/lib/neetob/cli/monthly_audit/security/github/dependabot_prs_merged.rb +20 -0
  55. data/lib/neetob/cli/monthly_audit/security/github/dependabot_turned_on.rb +25 -21
  56. data/lib/neetob/cli/neeto_deploy/autoscaling_config.rb +1 -1
  57. data/lib/neetob/cli/neeto_deploy/certificates.rb +1 -1
  58. data/lib/neetob/cli/neeto_deploy/commands.rb +7 -0
  59. data/lib/neetob/cli/neeto_deploy/config_vars/list.rb +1 -1
  60. data/lib/neetob/cli/neeto_deploy/config_vars/remove.rb +1 -1
  61. data/lib/neetob/cli/neeto_deploy/config_vars/upsert.rb +1 -1
  62. data/lib/neetob/cli/neeto_deploy/scheduled_exports.rb +1 -1
  63. data/lib/neetob/cli/neeto_deploy/unique_email_domains.rb +165 -0
  64. data/lib/neetob/cli/sre/base.rb +13 -13
  65. data/lib/neetob/cli/sre/check_essential_env.rb +7 -2
  66. data/lib/neetob/cli/sre/checklist.rb +2 -2
  67. data/lib/neetob/version.rb +1 -1
  68. data/neetob.gemspec +1 -1
  69. data/package.json +30 -0
  70. data/playwright.config.ts +39 -0
  71. data/scripts/config/.env.local +17 -0
  72. data/scripts/constants/auditData.ts +402 -0
  73. data/scripts/constants/routes.ts +30 -0
  74. data/scripts/constants/selectors.ts +4 -0
  75. data/scripts/constants/table.ts +30 -0
  76. data/scripts/constants/texts.ts +46 -0
  77. data/scripts/constants/userAgents.ts +14 -0
  78. data/scripts/utils/markdown.ts +23 -0
  79. data/scripts/workflows/dependabot.ts +104 -0
  80. data/scripts/workflows/honeybadger.ts +169 -0
  81. data/scripts/workflows/sparkpost.ts +204 -0
  82. data/tsconfig.json +35 -0
  83. data/yarn.lock +2216 -0
  84. metadata +26 -6
  85. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/bot_protection_enabled.rb +0 -32
  86. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/auto_scaling_enabled.rb +0 -60
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../github_issue_creation"
4
+ require "httparty"
5
+
3
6
  module Neetob
4
7
  class CLI
5
8
  module MonthlyAudit
@@ -24,6 +27,7 @@ module Neetob
24
27
  def initialize
25
28
  super()
26
29
  @resolver = Resolv::DNS.new
30
+ @all_flagged_certificates = []
27
31
  end
28
32
 
29
33
  def run
@@ -36,51 +40,80 @@ module Neetob
36
40
  audit_passed = nil
37
41
  comments = nil
38
42
  certificates_expiring_in_less_than_30_days = "No"
43
+
39
44
  if certificates_status.is_a?(Hash) && certificates_status["error"] == "Forbidden"
40
45
  audit_passed = "No"
41
46
  comments = "You do not have permission to access the certificates for this app."
42
47
  else
43
- certificates_to_be_flagged = certificates_status.select { |certificate|
48
+ current_app_flagged_certificates = certificates_status.select { |certificate|
44
49
  certificate["expires_before_30_days"] &&
45
- (certificate["domains"] || []).map {
46
- |domain| domain["hostname"]
47
- }.any? { |domain| should_flag_domain?(domain) }
50
+ (certificate["domains"] || []).map { |domain| domain["hostname"] }
51
+ .any? { |domain| should_flag_domain?(domain) }
48
52
  }
49
- audit_passed = certificates_to_be_flagged.empty? ? "Yes" : "No"
53
+ audit_passed = current_app_flagged_certificates.empty? ? "Yes" : "No"
50
54
  certificates_expiring_in_less_than_30_days = "Yes" if audit_passed == "No"
55
+
51
56
  if audit_passed == "No"
52
- certificates_failing_audit = certificates_to_be_flagged.map { |certificate|
57
+ @all_flagged_certificates.concat(current_app_flagged_certificates)
58
+ current_app_failing_certificates = current_app_flagged_certificates.map { |certificate|
53
59
  "#{certificate["name"]}(#{certificate["domains"].map { |domain| domain["hostname"] }.join(", ")})"
54
60
  }
55
- comments = "Certificates #{certificates_failing_audit.join(", ")} are expiring in less than 30 days."
61
+ comments = "Certificates #{current_app_failing_certificates.join(", ")} are expiring in less than 30 days."
56
62
  end
57
63
  end
64
+
58
65
  apps_data << [app, certificates_expiring_in_less_than_30_days, comments, audit_passed]
59
66
  end
60
- Neetob::CLI::Sre::Base::APPS_LIST[:heroku].select { |app| app.include?("production") }.each do |app|
61
- ui.info("Checking Certificates status for #{app}", print_to_audit_log: false)
62
- certificates_status = Neetob::CLI::Heroku::Certs.new(app).run
63
- certificates_expiring_in_less_than_30_days = certificates_status.select { |certificate| DateTime.parse(certificate[:expires]) <= 32.days.from_now }
64
- comments = nil
65
- audit_passed = "No"
66
- certificates_expiring_in_less_than_30_days_present = "No"
67
- if certificates_expiring_in_less_than_30_days.empty?
68
- audit_passed = "Yes"
69
- else
70
- comments = "Certificates #{certificates_expiring_in_less_than_30_days.map { |certificate| certificate[:name] }.join(", ")} are expiring in less than 30 days."
71
- certificates_expiring_in_less_than_30_days_present = "Yes"
67
+
68
+ if @all_flagged_certificates.any?
69
+ # categorize the certificates into bigbinary domains and custom domains
70
+ custom_domains = fetch_custom_domains.map { |domain| domain["hostname"] }
71
+ bigbinary_domains = Neetob::CLI::Cloudflare::Base::ZONE_IDS.keys.select { |domain| domain.to_s.include?(".com") }
72
+
73
+ flagged_by_category = {
74
+ bigbinary: select_flagged_by_domains(bigbinary_domains),
75
+ custom: select_flagged_by_domains(custom_domains)
76
+ }
77
+
78
+ issue_urls = []
79
+
80
+ unless flagged_by_category[:bigbinary].empty?
81
+ repo = "neeto-deploy-web"
82
+ comments = "Bigbinary domains SSL #{failing_comments(flagged_by_category[:bigbinary])}"
83
+ issue_urls << GithubIssueCreation.new.create_issue(
84
+ repo:,
85
+ title: "SSL certificates over 30 days from expiry",
86
+ description: comments
87
+ )
72
88
  end
73
- apps_data << [app, certificates_expiring_in_less_than_30_days_present, comments, audit_passed]
89
+
90
+ unless flagged_by_category[:custom].empty?
91
+ repo = "neeto-custom-domains-nano"
92
+ comments = failing_comments(flagged_by_category[:custom])
93
+ issue_urls << GithubIssueCreation.new.create_issue(
94
+ repo:,
95
+ title: "Custom domains SSL certificates over 30 days from expiry",
96
+ description: comments
97
+ )
98
+ end
99
+
100
+ ui.info("Issue created: #{issue_urls.join('<br>')}") if issue_urls.any?
74
101
  end
75
102
  ui.print_table(apps_data)
76
103
  end
77
104
 
78
105
  private
79
106
 
107
+ def fetch_custom_domains
108
+ headers = { "Authorization" => "Bearer #{ENV["NEETO_TOWER_AUTH_TOKEN"]}" }
109
+ response = HTTParty.get("https://ops.neetotower.com/api/v1/public/custom_domains", headers:)
110
+ response.parsed_response["domains"] || []
111
+ end
112
+
80
113
  def should_flag_domain?(hostname)
81
114
  valid_dns_configuration?(hostname) &&
82
- !points_to_heroku?(hostname) &&
83
- !DOMAINS_AUTO_RENEWED.include?(hostname)
115
+ !points_to_heroku?(hostname) &&
116
+ !DOMAINS_AUTO_RENEWED.include?(hostname)
84
117
  end
85
118
 
86
119
  def points_to_heroku?(hostname)
@@ -89,7 +122,7 @@ module Neetob
89
122
  cname_records.any? { |record| record.name.to_s.include?(HEROKU_DNS_TARGET) }
90
123
  end
91
124
  rescue Resolv::ResolvError, Timeout::Error => e
92
- ui.error("Heroku DNS resolution failed for #{hostname}: #{e.message}")
125
+ ui.error("Heroku DNS resolution failed for #{hostname}: #{e.message}", print_to_audit_log: false)
93
126
  false
94
127
  end
95
128
 
@@ -109,13 +142,25 @@ module Neetob
109
142
  false
110
143
  end
111
144
  rescue Resolv::ResolvError, Timeout::Error => e
112
- ui.error("Neetodeploy DNS resolution failed for #{hostname}: #{e.message}")
145
+ ui.error("Neetodeploy DNS resolution failed for #{hostname}: #{e.message}", print_to_audit_log: false)
113
146
  false
114
147
  end
115
148
 
116
149
  def root_level_domain?(hostname)
117
150
  hostname.split(".").length == 2
118
151
  end
152
+
153
+ def select_flagged_by_domains(domains)
154
+ @all_flagged_certificates.select do |cert|
155
+ cert["domains"].any? { |d| domains.include?(d["hostname"]) }
156
+ end
157
+ end
158
+
159
+ def failing_comments(certificates)
160
+ "Certificates #{certificates.map { |certificate|
161
+ "#{certificate["name"]}(#{certificate["domains"].map { |domain| domain["hostname"] }.join(", ")})"
162
+ }.join(", ")} are expiring in less than 30 days."
163
+ end
119
164
  end
120
165
  end
121
166
  end
@@ -15,7 +15,7 @@ module Neetob
15
15
  def run
16
16
  ui.success("# 4. Running miscellaneous checks")
17
17
  ui.info "\n"
18
- ui.success("## 4.1. [Manual] Checking whether we are using Sparkpost sub-account for all the apps")
18
+ ui.success("## 4.1. Checking whether we are using Sparkpost sub-account for all the apps")
19
19
  SparkpostSubAccountUsedForAllApps.new.run
20
20
  ui.info "\n"
21
21
  ui.success("## 4.2. Checking whether redirections are set correctly")
@@ -1,12 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../github_issue_creation"
4
+
3
5
  module Neetob
4
6
  class CLI
5
7
  module MonthlyAudit
6
8
  module Misc
7
9
  class RedirectionsWorkingCorrectly < CLI::Base
8
10
  REDIRECTIONS_LIST = [
9
- { source: "https://academy.bigbinary.com", destination: "https://bigbinaryacademy.com" }
11
+ {
12
+ source: "https://academy.bigbinary.com", destination: "https://bigbinaryacademy.com",
13
+ repo: "bigbinary-website"
14
+ },
10
15
  ]
11
16
  def initialize
12
17
  super()
@@ -19,6 +24,14 @@ module Neetob
19
24
  status = check.run ? "Working" : "Not working"
20
25
  audit_passed = status == "Working" ? "Yes" : "No"
21
26
 
27
+ if audit_passed == "No"
28
+ issue_url = GithubIssueCreation.new.create_issue(
29
+ repo: redirection[:repo], title: "Fix broken redirection",
30
+ description: "Redirection from #{redirection[:source]} to #{redirection[:destination]} is not working."
31
+ )
32
+ audit_passed += " #{issue_url}"
33
+ end
34
+
22
35
  table_data << [redirection[:source], redirection[:destination], status, audit_passed]
23
36
  end
24
37
  ui.print_table(table_data)
@@ -12,25 +12,31 @@ module Neetob
12
12
  end
13
13
 
14
14
  def run
15
- repo_data = [["App",
16
- "SPARKPOST_PASSWORD env variable first 4 characters same as corresponding sub-account in Sparkpost dashboard", "Comments", "Audit Passed"]]
17
- ui.info "\n"
18
- ui.info "#### Please manually check and add Yes/No for all the following checks:"
19
- ui.info "- Get the first 4 characters of the SPARKPOST_PASSWORD environment variable for the app"
20
- ui.info "- Go to https://app.sparkpost.com/account/api-keys"
21
- ui.info "- Match the first 4 characters against the API keys values"
22
- ui.info "- Verify that the API key belongs to the subaccount for same app. Accordingly add Yes/No in the 2nd column"
23
- ui.info "- Finally, set Audit Passed as Yes only if the last check passed, otherwise set it as No and add a comment in the Comments column"
24
- ui.info "\n"
25
- NeetoCompliance::NeetoRepos.products.keys.each do |repo|
26
- repo_data << (
27
- APPS_TO_IGNORE.include?(repo) ?
28
- [repo, "No", "App ignored from this check", "Ignored"] :
29
- [repo, nil, nil, nil]
30
- )
31
- end
32
- ui.print_table(repo_data)
15
+ result = run_sparkpost_check
16
+ formatted_result = extract_json_array_from_output(result)
17
+ create_issue(formatted_result)
18
+ ui.print_table(formatted_result)
33
19
  end
20
+
21
+ private
22
+
23
+ def run_sparkpost_check
24
+ `yarn audit:sparkpost`
25
+ end
26
+
27
+ def create_issue(formatted_result)
28
+ formatted_result.drop(1).each do |result|
29
+ repo = result[0]
30
+ audit_passed = result.last == "Yes"
31
+ if !audit_passed
32
+ comment = result[2]
33
+ issue_url = GithubIssueCreation.new.create_issue(
34
+ repo:, title: "Fix Sparkpost sub-account",
35
+ description: comment)
36
+ result[-1] += " #{issue_url}" if issue_url
37
+ end
38
+ end
39
+ end
34
40
  end
35
41
  end
36
42
  end
@@ -9,20 +9,25 @@ module Neetob
9
9
  class CLI
10
10
  module MonthlyAudit
11
11
  class Perform < CLI::Base
12
- attr_accessor :sandbox, :month
12
+ attr_accessor :sandbox, :month, :skip_issue
13
13
 
14
- def initialize(month, sandbox = false)
14
+ def initialize(month, sandbox = false, skip_issue = false)
15
15
  super()
16
16
  @month = month
17
17
  @sandbox = sandbox
18
+ @skip_issue = skip_issue
18
19
  end
19
20
 
20
21
  def run
22
+ Thread.current[:month] = month
23
+ Thread.current[:skip_issue] = skip_issue
21
24
  Thread.current[:audit_mode] = true
22
25
  markdown_file_name = "audit-report-#{DateTime.now.to_i}.md"
23
26
  Thread.current[:markdown_file_name] = markdown_file_name
24
27
  ui.success("## Starting the audit for #{month}")
25
28
  ui.info "\n"
29
+ ui.say("## Issue creation is #{skip_issue ? 'disabled' : 'enabled'}")
30
+ ui.info "\n"
26
31
  Security::Main.new.run
27
32
  ui.info "\n"
28
33
  Databases::Main.new.run
@@ -16,18 +16,23 @@ module Neetob
16
16
  ui.success("### 1.1.4. Checking whether running `rake active_record_doctor` throws any vulnerabilities")
17
17
  repo_data = [["Repository", "Issues Found", "Comments", "Audit Passed"]]
18
18
  ui.info "\n"
19
- NeetoCompliance::NeetoRepos.products.keys.take(5).each do |repo|
19
+ NeetoCompliance::NeetoRepos.products.keys.each do |repo|
20
20
  ui.info("Checking ActiveRecordDoctor run results for #{repo}", print_to_audit_log: false)
21
21
  active_record_doctor_run_result = Neetob::CLI::Github::ActiveRecordDoctor.new([repo]).run
22
-
22
+ audit_passed = "No"
23
23
  if active_record_doctor_run_result.blank?
24
- issues_found = "No"
24
+ audit_passed = "Yes"
25
25
  comments = nil
26
26
  else
27
27
  issues_found = "Yes"
28
- comments = "#{active_record_doctor_run_result.lines.first.strip} ..."
28
+ preview = active_record_doctor_run_result.lines.first(5).map(&:strip).reject(&:empty?).join("<br>")
29
+ comments = "#{preview} ... <br> (run `bundle exec rake active_record_doctor` in #{repo} to see all issues)"
30
+ issue_url = GithubIssueCreation.new.create_issue(
31
+ repo:, title: "Fix issues reported by active_record_doctor",
32
+ description: comments)
33
+ audit_passed += " #{issue_url}"
29
34
  end
30
- audit_passed = issues_found == "No" ? "Yes" : "No"
35
+
31
36
  repo_data << [repo, issues_found, comments, audit_passed]
32
37
  end
33
38
  ui.print_table(repo_data)
@@ -14,17 +14,25 @@ module Neetob
14
14
  ui.success("### 1.1.3. Checking whether running `bundle exec brakeman` throws any vulnerabilities")
15
15
  repo_data = [["Repository", "Vulnerabilities Found", "Comments", "Audit Passed"]]
16
16
  ui.info "\n"
17
- NeetoCompliance::NeetoRepos.products.keys.each do |repo|
17
+ products_and_nanos_repos(:backend).each do |repo|
18
18
  ui.info("Checking Brakeman run results for #{repo}", print_to_audit_log: false)
19
19
  brakeman_run_result = Neetob::CLI::Github::Brakeman.new([repo]).run
20
20
  vulnerabilities_found = "No"
21
21
  audit_passed = "No"
22
22
  comments = nil
23
- if brakeman_run_result && brakeman_run_result.include?("No warnings found")
23
+ if brakeman_run_result.nil? || brakeman_run_result.include?("No warnings found")
24
24
  audit_passed = "Yes"
25
25
  else
26
26
  vulnerabilities_found = "Yes"
27
27
  comments = brakeman_run_result.gsub("\n", "<br>")
28
+ issue_description = comments.split("== Warnings ==").last + " ... <br><br> (run `bundle exec brakeman` in #{repo} to see all issues)"
29
+ issue_url = GithubIssueCreation.new.create_issue(
30
+ repo:, title: "Fix vulnerabilities reported by Brakeman",
31
+ description: issue_description)
32
+ audit_passed += " #{issue_url}"
33
+ end
34
+ unless comments.nil?
35
+ comments = comments.split("<br>").last(7).join("<br>") + " ..."
28
36
  end
29
37
  repo_data << [repo, vulnerabilities_found, comments, audit_passed]
30
38
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../../../github/bundle_audit"
4
-
4
+ require_relative "../../github_issue_creation"
5
5
  module Neetob
6
6
  class CLI
7
7
  module MonthlyAudit
@@ -17,28 +17,41 @@ module Neetob
17
17
  repo_data = [["Repository", "Vulnerabilities Found", "Comments", "Audit Passed"]]
18
18
  ui.info "\n"
19
19
  last_comment = nil
20
- NeetoCompliance::NeetoRepos.products.keys.each do |repo|
20
+ products_and_nanos_repos(:backend).each do |repo|
21
21
  ui.info("Checking bundle audit run results for #{repo}", print_to_audit_log: false)
22
22
  bundle_audit_result = Neetob::CLI::Github::BundleAudit.new([repo]).run
23
23
  vulnerabilities_found = "No"
24
24
  audit_passed = "No"
25
25
  comments = nil
26
- if bundle_audit_result && bundle_audit_result.include?("No vulnerabilities found")
26
+ if bundle_audit_result.nil? || bundle_audit_result.include?("No vulnerabilities found")
27
27
  audit_passed = "Yes"
28
28
  else
29
- vulnerabilities_found = "Yes"
30
- comments = bundle_audit_result.gsub("\n", "<br>").gsub("~", "\\~")
31
29
 
30
+ vulnerabilities_found = "Yes"
31
+ formatted_result = bundle_audit_result.gsub("\n", "<br>").gsub("~", "\\~")
32
+ comments = formatted_result.split("<br><br>").first(3).map { |comment| parse_advisory_block(comment) }.join("<br><br>") + " ... <br><br> (run `bundle-audit check` in #{repo} to see all issues)"
33
+ issue_url = GithubIssueCreation.new.create_issue(
34
+ repo:, title: "Fix vulnerabilities reported by bundle-audit",
35
+ description: comments)
36
+ audit_passed += " #{issue_url}"
32
37
  same_as_last_vulnerabilities = comments == last_comment
33
38
  last_comment = comments
34
39
  if same_as_last_vulnerabilities
35
40
  comments = "''"
36
41
  end
37
42
  end
38
- repo_data << [repo, vulnerabilities_found, comments, audit_passed]
43
+ table_comments = comments.nil? ? nil : comments.split("<br>").first(3).join("<br>") + " ..."
44
+ repo_data << [repo, vulnerabilities_found, table_comments, audit_passed]
39
45
  end
40
46
  ui.print_table(repo_data)
41
47
  end
48
+
49
+ private
50
+
51
+ def parse_advisory_block(block)
52
+ keys = ["Name:", "Version:", "CVE:", "Title:", "Solution:"]
53
+ block.split("<br>").select { |line| keys.any? { |key| line.strip.start_with?(key) } }.join("<br>")
54
+ end
42
55
  end
43
56
  end
44
57
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../../../github/unused_assets_audit"
4
+ require_relative "../../github_issue_creation"
4
5
 
5
6
  module Neetob
6
7
  class CLI
@@ -24,6 +25,10 @@ module Neetob
24
25
  unused_assets_found = "Yes"
25
26
  audit_passed = "No"
26
27
  comments = unused_files.join("<br>")
28
+ issue_url = GithubIssueCreation.new.create_issue(
29
+ repo:, title: "Remove unused assets",
30
+ description: comments)
31
+ audit_passed += " #{issue_url}"
27
32
  else
28
33
  unused_assets_found = "No"
29
34
  audit_passed = "Yes"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../../../github/fasterer_audit"
4
+ require_relative "../../github_issue_creation"
4
5
 
5
6
  module Neetob
6
7
  class CLI
@@ -16,17 +17,24 @@ module Neetob
16
17
  ui.success("### 1.1.6. Checking whether running `bundle exec fasterer` give any suggestions")
17
18
  repo_data = [["Repository", "Optimizations needed", "Comments", "Audit Passed"]]
18
19
  ui.info "\n"
19
- NeetoCompliance::NeetoRepos.products.keys.each do |repo|
20
+ products_and_nanos_repos(:backend).each do |repo|
20
21
  ui.info("Checking Fasterer run results for #{repo}", print_to_audit_log: false)
21
22
  fasterer_exec_result = Neetob::CLI::Github::FastererAudit.new([repo]).run
22
23
  optimizations_needed = "No"
23
24
  audit_passed = "No"
24
25
  comments = nil
25
- if fasterer_exec_result.include?("0 offenses detected")
26
+ if fasterer_exec_result.nil? || fasterer_exec_result.include?("0 offenses detected")
26
27
  audit_passed = "Yes"
27
28
  comments = "Vulnerabilities not detected"
28
29
  else
29
30
  optimizations_needed = "Yes"
31
+ comments = strip_ansi_codes(fasterer_exec_result).gsub("\n", "<br>")
32
+
33
+ issue_url = GithubIssueCreation.new.create_issue(
34
+ repo:, title: "Fasterer suggestions to improve performance",
35
+ description: comments)
36
+ audit_passed += " #{issue_url}"
37
+
30
38
  comments = "Vulnerabilities detected"
31
39
  end
32
40
  repo_data << [repo, optimizations_needed, comments, audit_passed]
@@ -16,7 +16,7 @@ module Neetob
16
16
  ui.success("### 1.1.2. Checking whether running `yarn audit` throws any vulnerabilities")
17
17
  repo_data = [["Repository", "Vulnerabilities Found", "Comments", "Audit Passed"]]
18
18
  ui.info "\n"
19
- NeetoCompliance::NeetoRepos.products.keys.each do |repo|
19
+ products_and_nanos_repos(:frontend).each do |repo|
20
20
  ui.info("Checking yarn audit run results for #{repo}", print_to_audit_log: false)
21
21
  yarn_audit_result = Neetob::CLI::Github::YarnAudit.new([repo]).run
22
22
  vulnerabilities_found = "No"
@@ -31,6 +31,11 @@ line.include?("Severity:") }.first&.strip&.gsub("|", ",")
31
31
  vulnerabilities = yarn_audit_result.split("\n").select { |line|
32
32
  line.include?("vulnerabilities found") }.first&.strip
33
33
  comments = "#{vulnerabilities}<br>#{severity}"
34
+
35
+ issue_url = GithubIssueCreation.new.create_issue(
36
+ repo:, title: "Yarn audit: High/Critical vulnerabilities found",
37
+ description: comments)
38
+ audit_passed += " #{issue_url}"
34
39
  end
35
40
  repo_data << [repo, vulnerabilities_found, comments, audit_passed]
36
41
  end
@@ -31,10 +31,30 @@ module Neetob
31
31
  dependabot_prs_older_than_2_days_merged = "No"
32
32
  audit_passed = "No"
33
33
  comments = nil
34
+ pr_urls = []
35
+
34
36
  if dependabot_prs_older_than_2_days.empty?
35
37
  audit_passed = dependabot_prs_older_than_2_days_merged = "Yes"
36
38
  else
37
39
  comments = "PRs older than 2 days: #{dependabot_prs_older_than_2_days.map { |pr| pr[:number] }.join(', ')}"
40
+
41
+ # Add comments to each Dependabot PR and collect URLs
42
+ pull_requests_client = Neetob::CLI::Github::Repositories::PullRequests.new([repo])
43
+ dependabot_prs_older_than_2_days.each do |pr|
44
+ comment_body = "⚠️ **Monthly Audit Alert** ⚠️\n\n" \
45
+ "This Dependabot PR has been open for more than 2 days. " \
46
+ "Please review and merge this PR to keep dependencies up to date.\n\n" \
47
+ "*This comment was automatically added by the monthly security audit.*"
48
+
49
+ assignees = [Neetob::CLI::Github::Repositories::TeamLeads.team_lead_for(repo)]
50
+
51
+ pr_url = pull_requests_client.add_comment_to_pr(
52
+ prefix_org_name([repo]).first, pr[:number],
53
+ comment_body, assignees)
54
+ pr_urls << pr_url if pr_url
55
+ end
56
+
57
+ audit_passed = "No #{pr_urls.join(', ')}"
38
58
  end
39
59
  repo_data << [repo, dependabot_prs_older_than_2_days_merged, comments, audit_passed]
40
60
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../../../github/repositories/get_security_details.rb"
4
+ require "json"
4
5
 
5
6
  module Neetob
6
7
  class CLI
@@ -13,29 +14,32 @@ module Neetob
13
14
  end
14
15
 
15
16
  def run
16
- ui.success("### 1.2.1. [Manual] Checking whether dependabot is turned on for the repository")
17
- ui.info "\n"
18
- ui.info "#### Please manually check and add Yes/No for all the following checks on the Honeybadger dashboard for the apps listed in the table below:"
19
- ui.info "- Repository > Settings > Code security > Dependabot alerts are enabled"
20
- ui.info "- Repository > Settings > Code security > Dependabot security updates are enabled"
21
- ui.info "- Repository > Settings > Actions > Dependabot on Actions runners is enabled"
22
- ui.info "- Finally, set Audit Passed as Yes only if all the checks are passed for the app, otherwise set it as No, and add a comment in the Comments column"
23
- ui.info "\n"
17
+ ui.success("### 1.2.1. Checking whether dependabot is turned on for the repository")
18
+ result = run_dependabot_check
19
+ formatted_result = extract_json_array_from_output(result)
20
+ create_issue(formatted_result)
21
+ ui.print_table(formatted_result)
22
+ end
23
+
24
+ private
24
25
 
25
- repo_data = [[
26
- "Repository",
27
- "Dependabot alerts enabled",
28
- "Dependabot security updates enabled",
29
- "Dependabot on Actions runners",
30
- "Comments",
31
- "Audit Passed"
32
- ]
33
- ]
34
- NeetoCompliance::NeetoRepos.products.keys.each do |repo|
35
- repo_data << [repo, nil, nil, nil, nil]
26
+ def run_dependabot_check
27
+ `yarn audit:dependabot`
28
+ end
29
+
30
+ def create_issue(formatted_result)
31
+ formatted_result.drop(1).each do |result|
32
+ repo = result[0].split("]")[0].gsub("[", "")
33
+ audit_passed = result.last == "Yes"
34
+ if !audit_passed
35
+ comment = "Dependabot alerts or security updates are not enabled for this repository."
36
+ issue_url = GithubIssueCreation.new.create_issue(
37
+ repo:, title: "Fix Dependabot settings",
38
+ description: comment)
39
+ result[-1] += " #{issue_url}" if issue_url
40
+ end
41
+ end
36
42
  end
37
- ui.print_table(repo_data)
38
- end
39
43
  end
40
44
  end
41
45
  end
@@ -12,7 +12,7 @@ module Neetob
12
12
  end
13
13
 
14
14
  def run
15
- result = `neetodeploy autoscaling_config list -a #{app}`
15
+ result = `bundle exec neetodeploy autoscaling_config list -a #{app}`
16
16
  if Thread.current[:audit_mode]
17
17
  JSON.parse(result) rescue result
18
18
  else
@@ -14,7 +14,7 @@ module Neetob
14
14
  end
15
15
 
16
16
  def run
17
- certificates_command_result = `neetodeploy certificates list -a #{app}`
17
+ certificates_command_result = `bundle exec neetodeploy certificates list -a #{app}`
18
18
  parseable_result = certificates_command_result.strip.gsub("=>", ":")
19
19
 
20
20
  parsed_certificates_result = JSON.parse(parseable_result)
@@ -5,6 +5,7 @@ require_relative "config_vars/commands"
5
5
  require_relative "./autoscaling_config"
6
6
  require_relative "./scheduled_exports"
7
7
  require_relative "./certificates"
8
+ require_relative "./unique_email_domains"
8
9
 
9
10
  module Neetob
10
11
  class CLI
@@ -30,6 +31,12 @@ module Neetob
30
31
  def certificates
31
32
  Certificates.new(options[:app]).run
32
33
  end
34
+
35
+ desc "unique_email_domains", "Get the unique email domains for an app deployed on NeetoDeploy"
36
+ option :app, type: :string, aliases: :a, desc: "App name"
37
+ def unique_email_domains
38
+ UniqueEmailDomains.new(options[:app]).run
39
+ end
33
40
  end
34
41
  end
35
42
  end
@@ -20,7 +20,7 @@ module Neetob
20
20
  results = []
21
21
  matching_apps.each do |app|
22
22
  ui.info("\nConfig of #{app}\n") unless Thread.current[:audit_mode]
23
- result = `neetodeploy env list -a #{app}`
23
+ result = `bundle exec neetodeploy env list -a #{app}`
24
24
  results << result
25
25
  ui.success(result) unless Thread.current[:audit_mode]
26
26
  end
@@ -21,7 +21,7 @@ module Neetob
21
21
  matching_apps.each do |app|
22
22
  ui.info("\nWorking on #{app}")
23
23
  keys.each do |key|
24
- ui.info(`neetodeploy env unset #{key} -a #{app}`)
24
+ ui.info(`bundle exec neetodeploy env unset #{key} -a #{app}`)
25
25
  end
26
26
  end
27
27
  end
@@ -48,7 +48,7 @@ required_config_vars_with_project_keys_json_file_path, sandbox = false)
48
48
  def upsert_config_variables(app, vars)
49
49
  ui.info("\nWorking on #{app}")
50
50
  vars.each do |key, val|
51
- ui.info(`neetodeploy env set #{key}='#{val}' -a #{app}`)
51
+ ui.info(`bundle exec neetodeploy env set #{key}='#{val}' -a #{app}`)
52
52
  end
53
53
  end
54
54