neetob 0.5.69 → 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 (72) 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 +35 -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 +17 -4
  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/github_issue_creation.rb +57 -17
  24. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/{bot_protection_enabled.rb → automatic_https_rewrites_is_enabled.rb} +11 -9
  25. data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/main.rb +2 -2
  26. data/lib/neetob/cli/monthly_audit/instances_and_addons/cronitor/setup_correctly_for_landing_pages.rb +3 -2
  27. data/lib/neetob/cli/monthly_audit/instances_and_addons/honeybadger/setup_correctly_for_apps.rb +28 -29
  28. data/lib/neetob/cli/monthly_audit/instances_and_addons/main.rb +5 -5
  29. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/cloudfront_cdn_enabled.rb +0 -17
  30. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/essential_environment_variables_set.rb +0 -15
  31. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/main.rb +0 -3
  32. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/scheduled_exports_enabled.rb +2 -4
  33. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/ssl_certificates_over_thirty_days_from_expiry.rb +67 -34
  34. data/lib/neetob/cli/monthly_audit/misc/main.rb +1 -1
  35. data/lib/neetob/cli/monthly_audit/misc/sparkpost_sub_account_used_for_all_apps.rb +24 -18
  36. data/lib/neetob/cli/monthly_audit/security/code/active_record_doctor.rb +2 -2
  37. data/lib/neetob/cli/monthly_audit/security/code/brakeman.rb +7 -4
  38. data/lib/neetob/cli/monthly_audit/security/code/bundle_audit.rb +13 -4
  39. data/lib/neetob/cli/monthly_audit/security/code/fasterer.rb +2 -2
  40. data/lib/neetob/cli/monthly_audit/security/code/yarn_audit.rb +1 -1
  41. data/lib/neetob/cli/monthly_audit/security/github/dependabot_prs_merged.rb +20 -5
  42. data/lib/neetob/cli/monthly_audit/security/github/dependabot_turned_on.rb +25 -21
  43. data/lib/neetob/cli/neeto_deploy/autoscaling_config.rb +1 -1
  44. data/lib/neetob/cli/neeto_deploy/certificates.rb +1 -1
  45. data/lib/neetob/cli/neeto_deploy/commands.rb +7 -0
  46. data/lib/neetob/cli/neeto_deploy/config_vars/list.rb +1 -1
  47. data/lib/neetob/cli/neeto_deploy/config_vars/remove.rb +1 -1
  48. data/lib/neetob/cli/neeto_deploy/config_vars/upsert.rb +1 -1
  49. data/lib/neetob/cli/neeto_deploy/scheduled_exports.rb +1 -1
  50. data/lib/neetob/cli/neeto_deploy/unique_email_domains.rb +165 -0
  51. data/lib/neetob/cli/sre/base.rb +13 -13
  52. data/lib/neetob/cli/sre/check_essential_env.rb +7 -2
  53. data/lib/neetob/cli/sre/checklist.rb +2 -2
  54. data/lib/neetob/version.rb +1 -1
  55. data/neetob.gemspec +1 -1
  56. data/package.json +30 -0
  57. data/playwright.config.ts +39 -0
  58. data/scripts/config/.env.local +17 -0
  59. data/scripts/constants/auditData.ts +402 -0
  60. data/scripts/constants/routes.ts +30 -0
  61. data/scripts/constants/selectors.ts +4 -0
  62. data/scripts/constants/table.ts +30 -0
  63. data/scripts/constants/texts.ts +46 -0
  64. data/scripts/constants/userAgents.ts +14 -0
  65. data/scripts/utils/markdown.ts +23 -0
  66. data/scripts/workflows/dependabot.ts +104 -0
  67. data/scripts/workflows/honeybadger.ts +169 -0
  68. data/scripts/workflows/sparkpost.ts +204 -0
  69. data/tsconfig.json +35 -0
  70. data/yarn.lock +2216 -0
  71. metadata +24 -6
  72. data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/auto_scaling_enabled.rb +0 -67
@@ -1,35 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../../github_issue_creation"
4
+
4
5
  module Neetob
5
6
  class CLI
6
7
  module MonthlyAudit
7
8
  module InstancesAndAddons
8
9
  module Cloudflare
9
- class BotProtectionEnabled < CLI::Base
10
+ class AutomaticHttpsRewritesIsEnabled < CLI::Base
10
11
  def initialize
11
12
  super()
12
13
  end
13
14
 
14
15
  def run
15
- ui.success "### 3.2.6. Checking whether Bot Protection is enabled"
16
+ ui.success "### 3.2.7. Checking whether Automatic HTTPS rewrites is enabled"
16
17
 
17
- domains_data = [["Domain", "Bot protection", "Audit Passed"]]
18
+ domains_data = [["Domain", "Automatic HTTPS rewrites", "Audit Passed"]]
18
19
  ui.info("\n", print_to_audit_log: false)
19
20
  Neetob::CLI::Cloudflare::Base::ZONE_IDS.keys.select { |domain|
20
21
  domain.to_s.include?(".com") }.map do |domain|
21
- bot_fight_mode = Neetob::CLI::Cloudflare::BotFightMode.new(domain).run
22
- ui.info("Checking Bot fight mode for #{domain}", print_to_audit_log: false)
23
- audit_passed = bot_fight_mode == "on" ? "Yes" : "No"
22
+ ui.info("Checking automatic HTTPS rewrites value for #{domain}", print_to_audit_log: false)
23
+ automatic_https_rewrites_value = Neetob::CLI::Cloudflare::AutomaticHttpsRewrites.new(domain).run
24
+ audit_passed = automatic_https_rewrites_value.to_s == "on" ? "Yes" : "No"
24
25
 
25
26
  if audit_passed == "No"
26
27
  repo = domain_to_repo(domain.to_s)
27
28
  issue_url = GithubIssueCreation.new.create_issue(
28
- repo:, title: "Enable bot protection for #{domain}",
29
- description: "Bot Protection is not enabled for #{domain}.")
29
+ repo:, title: "Enable automatic HTTPS rewrites for domain",
30
+ description: "Automatic HTTPS rewrites is not enabled for #{domain}.")
30
31
  audit_passed += " #{issue_url}"
31
32
  end
32
- domains_data << [domain, bot_fight_mode, audit_passed]
33
+
34
+ domains_data << [domain, automatic_https_rewrites_value, audit_passed]
33
35
  end
34
36
  ui.print_table(domains_data)
35
37
  end
@@ -5,7 +5,7 @@ require_relative "dns_entry_has_proxy_status"
5
5
  require_relative "minimum_tls_version_is_one_point_two"
6
6
  require_relative "always_use_https_is_enabled"
7
7
  require_relative "spf_records_are_valid"
8
- require_relative "bot_protection_enabled"
8
+ require_relative "automatic_https_rewrites_is_enabled"
9
9
 
10
10
  module Neetob
11
11
  class CLI
@@ -28,7 +28,7 @@ module Neetob
28
28
  ui.info "\n"
29
29
  SpfRecordsAreValid.new.run
30
30
  ui.info "\n"
31
- BotProtectionEnabled.new.run
31
+ AutomaticHttpsRewritesIsEnabled.new.run
32
32
  end
33
33
  end
34
34
  end
@@ -10,8 +10,9 @@ module Neetob
10
10
  class SetupCorrectlyForLandingPages < CLI::Base
11
11
  APPS_TO_IGNORE = [
12
12
  "NeetoAuth",
13
- "NeetoTower"
14
- ]
13
+ "NeetoTower",
14
+ "NeetoCode"
15
+ ].freeze
15
16
 
16
17
  attr_reader :all_monitors
17
18
 
@@ -11,36 +11,35 @@ module Neetob
11
11
  end
12
12
 
13
13
  def run
14
- ui.success "### 3.4.1. [Manual] Checking whether Honeybadger is correctly set up for apps"
15
- 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:"
16
- ui.info "- Settings > General > Project Settings > Enable notifications for production is turned on"
17
- ui.info "- Settings > Alerts & integrations > This project's integrations > GitHub is integrated to the correct project repo."
18
- ui.info "- Settings > Alerts & integrations > This project's integrations > GitHub > Edit > Error Events > Automatically create an issue when an error occurs and Automatically re-open issues are turned on"
19
- ui.info "- Settings > Alerts & integrations > This project's integrations > Slack is integrated to the correct project channel in #neeto-health workspace"
20
- ui.info "- Please add comments if any for the checks below in the Comments column in the table"
21
- 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"
22
-
23
- apps_data = [
24
- [
25
- "App",
26
- "Enable Notifications for production is turned on",
27
- "GitHub is integrated to the correct project repo",
28
- "Automatically create GitHub issue and automatically re-open issue is turned on",
29
- "Slack is integrated to the correct project channel in #neeto-health workspace",
30
- "Comments",
31
- "Audit Passed"
32
- ]
33
- ]
34
- ui.info("\n")
35
- (
36
- Neetob::CLI::Sre::Base::APPS_LIST[:neetodeploy] +
37
- Neetob::CLI::Sre::Base::APPS_LIST[:heroku]
38
- ).select { |app| app.include?("production") }.each do |app|
39
-
40
- apps_data << [app, nil, nil, nil, nil, nil, nil]
41
- end
42
- ui.print_table(apps_data)
14
+ ui.success "### 3.4.1. Checking whether Honeybadger is correctly set up for apps"
15
+
16
+ result = run_honeybadger_check
17
+ formatted_result = extract_json_array_from_output(result)
18
+
19
+ create_issue(formatted_result)
20
+
21
+ ui.print_table(formatted_result)
43
22
  end
23
+
24
+ private
25
+
26
+ def run_honeybadger_check
27
+ `yarn audit:honeybadger`
28
+ end
29
+
30
+ def create_issue(formatted_result)
31
+ formatted_result.drop(1).each do |result|
32
+ repo = result[0].gsub("-production", "")
33
+ audit_passed = result.last == "Yes"
34
+ if !audit_passed
35
+ comment = result[5]
36
+ issue_url = GithubIssueCreation.new.create_issue(
37
+ repo:, title: "Fix Honeybadger settings",
38
+ description: comment)
39
+ result[-1] += " #{issue_url}" if issue_url
40
+ end
41
+ end
42
+ end
44
43
  end
45
44
  end
46
45
  end
@@ -24,12 +24,12 @@ module Neetob
24
24
  ui.success("## 3.2. Checking Cloudflare related configurations")
25
25
  ui.info "\n"
26
26
  Cloudflare::Main.new.run
27
+ # ui.info "\n"
28
+ # ui.success("## 3.3. Checking Cronitor related configurations")
29
+ # ui.info "\n"
30
+ # Cronitor::Main.new.run
27
31
  ui.info "\n"
28
- ui.success("## 3.3. Checking Cronitor related configurations")
29
- ui.info "\n"
30
- Cronitor::Main.new.run
31
- ui.info "\n"
32
- ui.success("## 3.4. Checking Honeybadger related configurations")
32
+ ui.success("## 3.3. Checking Honeybadger related configurations")
33
33
  ui.info "\n"
34
34
  Honeybadger::Main.new.run
35
35
  end
@@ -57,23 +57,6 @@ module Neetob
57
57
 
58
58
  apps_data << [app, asset_host_value, comments, audit_passed]
59
59
  end
60
- Neetob::CLI::Sre::Base::APPS_LIST[:heroku].select { |app| app.include?("production") }.each do |app|
61
- ui.info("Checking ASSET_HOST value for #{app}", print_to_audit_log: false)
62
- config_vars_result = Neetob::CLI::Heroku::ConfigVars::List.new([app]).run[0]
63
- asset_host_value = config_vars_result["ASSET_HOST"]
64
- if asset_host_value.nil?
65
- audit_passed = "No"
66
- comments = "ASSET_HOST value not found."
67
- else
68
- is_direct_cloudfront_asset_host = asset_host_value.include?("cloudfront.net")
69
- is_cdn_subdomain_asset_host = asset_host_value == "cdn.#{app.gsub("-web-production", "").gsub("-", "")}.com"
70
- audit_passed = is_direct_cloudfront_asset_host || is_cdn_subdomain_asset_host ? "Yes" : "No"
71
- if audit_passed == "No"
72
- comments = "ASSET_HOST value is not a Cloudfront CDN URL or a CDN subdomain URL."
73
- end
74
- end
75
- apps_data << [app, asset_host_value, comments, audit_passed]
76
- end
77
60
  ui.print_table(apps_data)
78
61
  end
79
62
  end
@@ -40,21 +40,6 @@ module Neetob
40
40
  end
41
41
  apps_data << [app, all_essential_env_variables_set, comments, audit_passed]
42
42
  end
43
- Neetob::CLI::Sre::Base::APPS_LIST[:heroku].select { |app| app.include?("production") }.each do |app|
44
- ui.info("Checking essential env variables for #{app}", print_to_audit_log: false)
45
- essential_env_variables_result = Neetob::CLI::Sre::CheckEssentialEnv.new(app).run
46
- all_essential_env_variables_set = essential_env_variables_result[:all_keys_present]
47
- audit_passed = all_essential_env_variables_set ? "Yes" : "No"
48
- repo = app.gsub("-production", "")
49
- if audit_passed == "No"
50
- comments = "Missing keys: #{essential_env_variables_result[:missing_keys].join(", ")}"
51
- issue_url = GithubIssueCreation.new.create_issue(
52
- repo:, title: "Add missing essential environment variables",
53
- description: comments)
54
- audit_passed += " #{issue_url}"
55
- end
56
- apps_data << [app, all_essential_env_variables_set, comments, audit_passed]
57
- end
58
43
  ui.print_table(apps_data)
59
44
  end
60
45
  end
@@ -3,7 +3,6 @@
3
3
  require_relative "ssl_certificates_over_thirty_days_from_expiry"
4
4
  require_relative "cloudfront_cdn_enabled"
5
5
  require_relative "essential_environment_variables_set"
6
- require_relative "auto_scaling_enabled"
7
6
  require_relative "scheduled_exports_enabled"
8
7
 
9
8
  module Neetob
@@ -23,8 +22,6 @@ module Neetob
23
22
  ui.info "\n"
24
23
  EssentialEnvironmentVariablesSet.new.run
25
24
  ui.info "\n"
26
- AutoScalingEnabled.new.run
27
- ui.info "\n"
28
25
  ScheduledExportsEnabled.new.run
29
26
  end
30
27
  end
@@ -12,7 +12,7 @@ module Neetob
12
12
  end
13
13
 
14
14
  def run
15
- ui.success "### 3.1.5. Checking whether scheduled exports are enabled"
15
+ ui.success "### 3.1.4. Checking whether scheduled exports are enabled"
16
16
 
17
17
  apps_data = [["App", "Scheduled exports config", "Comments", "Audit Passed"]]
18
18
  ui.info("\n", print_to_audit_log: false)
@@ -28,9 +28,7 @@ module Neetob
28
28
  audit_passed = "No"
29
29
  comments = "You do not have permission to access the config for this app."
30
30
  else
31
- scheduled_exports_config = scheduled_exports_result
32
- .gsub("\e[32m", "")
33
- .gsub("\e[0m", "")
31
+ scheduled_exports_config = scheduled_exports_config = strip_ansi_codes(scheduled_exports_result)
34
32
  .gsub("#{app}'s", "")
35
33
  .strip
36
34
  audit_passed = scheduled_exports_config.include?("turned on") ? "Yes" : "No"
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../../github_issue_creation"
4
+ require "httparty"
4
5
 
5
6
  module Neetob
6
7
  class CLI
@@ -26,6 +27,7 @@ module Neetob
26
27
  def initialize
27
28
  super()
28
29
  @resolver = Resolv::DNS.new
30
+ @all_flagged_certificates = []
29
31
  end
30
32
 
31
33
  def run
@@ -38,61 +40,80 @@ module Neetob
38
40
  audit_passed = nil
39
41
  comments = nil
40
42
  certificates_expiring_in_less_than_30_days = "No"
43
+
41
44
  if certificates_status.is_a?(Hash) && certificates_status["error"] == "Forbidden"
42
45
  audit_passed = "No"
43
46
  comments = "You do not have permission to access the certificates for this app."
44
47
  else
45
- certificates_to_be_flagged = certificates_status.select { |certificate|
48
+ current_app_flagged_certificates = certificates_status.select { |certificate|
46
49
  certificate["expires_before_30_days"] &&
47
- (certificate["domains"] || []).map {
48
- |domain| domain["hostname"]
49
- }.any? { |domain| should_flag_domain?(domain) }
50
+ (certificate["domains"] || []).map { |domain| domain["hostname"] }
51
+ .any? { |domain| should_flag_domain?(domain) }
50
52
  }
51
- audit_passed = certificates_to_be_flagged.empty? ? "Yes" : "No"
53
+ audit_passed = current_app_flagged_certificates.empty? ? "Yes" : "No"
52
54
  certificates_expiring_in_less_than_30_days = "Yes" if audit_passed == "No"
55
+
53
56
  if audit_passed == "No"
54
- 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|
55
59
  "#{certificate["name"]}(#{certificate["domains"].map { |domain| domain["hostname"] }.join(", ")})"
56
60
  }
57
- comments = "Certificates #{certificates_failing_audit.join(", ")} are expiring in less than 30 days."
58
- repo = app.gsub("-production", "")
59
- issue_url = GithubIssueCreation.new.create_issue(
60
- repo:, title: "SSL certificates over 30 days from expiry",
61
- description: comments)
62
- audit_passed += " #{issue_url}"
61
+ comments = "Certificates #{current_app_failing_certificates.join(", ")} are expiring in less than 30 days."
63
62
  end
64
63
  end
64
+
65
65
  apps_data << [app, certificates_expiring_in_less_than_30_days, comments, audit_passed]
66
66
  end
67
- Neetob::CLI::Sre::Base::APPS_LIST[:heroku].select { |app| app.include?("production") }.each do |app|
68
- ui.info("Checking Certificates status for #{app}", print_to_audit_log: false)
69
- certificates_status = Neetob::CLI::Heroku::Certs.new(app).run
70
- certificates_expiring_in_less_than_30_days = certificates_status.select { |certificate| DateTime.parse(certificate[:expires]) <= 32.days.from_now }
71
- comments = nil
72
- audit_passed = "No"
73
- certificates_expiring_in_less_than_30_days_present = "No"
74
- if certificates_expiring_in_less_than_30_days.empty?
75
- audit_passed = "Yes"
76
- else
77
- comments = "Certificates #{certificates_expiring_in_less_than_30_days.map { |certificate| certificate[:name] }.join(", ")} are expiring in less than 30 days."
78
- repo = app.gsub("-production", "")
79
- issue_url = GithubIssueCreation.new.create_issue(
80
- repo:, title: "Renew SSL certificates expiring in less than 30 days",
81
- description: comments)
82
- audit_passed += " #{issue_url}"
83
- 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
+ )
84
88
  end
85
- 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?
86
101
  end
87
102
  ui.print_table(apps_data)
88
103
  end
89
104
 
90
105
  private
91
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
+
92
113
  def should_flag_domain?(hostname)
93
114
  valid_dns_configuration?(hostname) &&
94
- !points_to_heroku?(hostname) &&
95
- !DOMAINS_AUTO_RENEWED.include?(hostname)
115
+ !points_to_heroku?(hostname) &&
116
+ !DOMAINS_AUTO_RENEWED.include?(hostname)
96
117
  end
97
118
 
98
119
  def points_to_heroku?(hostname)
@@ -101,7 +122,7 @@ module Neetob
101
122
  cname_records.any? { |record| record.name.to_s.include?(HEROKU_DNS_TARGET) }
102
123
  end
103
124
  rescue Resolv::ResolvError, Timeout::Error => e
104
- 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)
105
126
  false
106
127
  end
107
128
 
@@ -121,13 +142,25 @@ module Neetob
121
142
  false
122
143
  end
123
144
  rescue Resolv::ResolvError, Timeout::Error => e
124
- 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)
125
146
  false
126
147
  end
127
148
 
128
149
  def root_level_domain?(hostname)
129
150
  hostname.split(".").length == 2
130
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
131
164
  end
132
165
  end
133
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")
@@ -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
@@ -25,8 +25,8 @@ module Neetob
25
25
  comments = nil
26
26
  else
27
27
  issues_found = "Yes"
28
- comments = "#{active_record_doctor_run_result.lines.first.strip} ..."
29
-
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
30
  issue_url = GithubIssueCreation.new.create_issue(
31
31
  repo:, title: "Fix issues reported by active_record_doctor",
32
32
  description: comments)
@@ -14,23 +14,26 @@ 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
-
28
+ issue_description = comments.split("== Warnings ==").last + " ... <br><br> (run `bundle exec brakeman` in #{repo} to see all issues)"
29
29
  issue_url = GithubIssueCreation.new.create_issue(
30
30
  repo:, title: "Fix vulnerabilities reported by Brakeman",
31
- description: comments)
31
+ description: issue_description)
32
32
  audit_passed += " #{issue_url}"
33
33
  end
34
+ unless comments.nil?
35
+ comments = comments.split("<br>").last(7).join("<br>") + " ..."
36
+ end
34
37
  repo_data << [repo, vulnerabilities_found, comments, audit_passed]
35
38
  end
36
39
  ui.print_table(repo_data)
@@ -17,18 +17,19 @@ 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
29
 
30
30
  vulnerabilities_found = "Yes"
31
- comments = bundle_audit_result.gsub("\n", "<br>").gsub("~", "\\~")
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)"
32
33
  issue_url = GithubIssueCreation.new.create_issue(
33
34
  repo:, title: "Fix vulnerabilities reported by bundle-audit",
34
35
  description: comments)
@@ -39,10 +40,18 @@ module Neetob
39
40
  comments = "''"
40
41
  end
41
42
  end
42
- 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]
43
45
  end
44
46
  ui.print_table(repo_data)
45
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
46
55
  end
47
56
  end
48
57
  end
@@ -17,13 +17,13 @@ module Neetob
17
17
  ui.success("### 1.1.6. Checking whether running `bundle exec fasterer` give any suggestions")
18
18
  repo_data = [["Repository", "Optimizations needed", "Comments", "Audit Passed"]]
19
19
  ui.info "\n"
20
- NeetoCompliance::NeetoRepos.products.keys.each do |repo|
20
+ products_and_nanos_repos(:backend).each do |repo|
21
21
  ui.info("Checking Fasterer run results for #{repo}", print_to_audit_log: false)
22
22
  fasterer_exec_result = Neetob::CLI::Github::FastererAudit.new([repo]).run
23
23
  optimizations_needed = "No"
24
24
  audit_passed = "No"
25
25
  comments = nil
26
- if fasterer_exec_result.include?("0 offenses detected")
26
+ if fasterer_exec_result.nil? || fasterer_exec_result.include?("0 offenses detected")
27
27
  audit_passed = "Yes"
28
28
  comments = "Vulnerabilities not detected"
29
29
  else
@@ -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"
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../../../github/repositories/pull_requests"
4
- require_relative "../../github_issue_creation"
5
4
 
6
5
  module Neetob
7
6
  class CLI
@@ -32,14 +31,30 @@ module Neetob
32
31
  dependabot_prs_older_than_2_days_merged = "No"
33
32
  audit_passed = "No"
34
33
  comments = nil
34
+ pr_urls = []
35
+
35
36
  if dependabot_prs_older_than_2_days.empty?
36
37
  audit_passed = dependabot_prs_older_than_2_days_merged = "Yes"
37
38
  else
38
39
  comments = "PRs older than 2 days: #{dependabot_prs_older_than_2_days.map { |pr| pr[:number] }.join(', ')}"
39
- issue_url = GithubIssueCreation.new.create_issue(
40
- repo:, title: "Merge Dependabot PRs older than 2 days",
41
- description: comments)
42
- audit_passed += " #{issue_url}"
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(', ')}"
43
58
  end
44
59
  repo_data << [repo, dependabot_prs_older_than_2_days_merged, comments, audit_passed]
45
60
  end