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.
- checksums.yaml +4 -4
- data/.env +2 -1
- data/.neetoci/default.yml +1 -1
- data/.ruby-version +1 -1
- data/Gemfile.lock +44 -21
- data/README.md +11 -0
- data/bookmarks.md +113 -113
- data/data/github-labels.json +80 -45
- data/data/repo-team-leads.json +82 -0
- data/exe/neetob +1 -1
- data/lib/neetob/cli/base.rb +35 -5
- data/lib/neetob/cli/cloudflare/automatic_https_rewrites.rb +34 -0
- data/lib/neetob/cli/cloudflare/base.rb +2 -2
- data/lib/neetob/cli/cloudflare/commands.rb +7 -0
- data/lib/neetob/cli/github/active_record_doctor.rb +1 -1
- data/lib/neetob/cli/github/brakeman.rb +1 -1
- data/lib/neetob/cli/github/bundle_audit.rb +1 -1
- data/lib/neetob/cli/github/issues/helpers.rb +17 -4
- data/lib/neetob/cli/github/make_pr/base.rb +1 -1
- data/lib/neetob/cli/github/repositories/pull_requests.rb +19 -0
- data/lib/neetob/cli/github/repositories/team_leads.rb +34 -0
- data/lib/neetob/cli/github/unused_assets_audit.rb +5 -1
- data/lib/neetob/cli/monthly_audit/github_issue_creation.rb +57 -17
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/{bot_protection_enabled.rb → automatic_https_rewrites_is_enabled.rb} +11 -9
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/main.rb +2 -2
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cronitor/setup_correctly_for_landing_pages.rb +3 -2
- data/lib/neetob/cli/monthly_audit/instances_and_addons/honeybadger/setup_correctly_for_apps.rb +28 -29
- data/lib/neetob/cli/monthly_audit/instances_and_addons/main.rb +5 -5
- data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/cloudfront_cdn_enabled.rb +0 -17
- data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/essential_environment_variables_set.rb +0 -15
- data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/main.rb +0 -3
- data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/scheduled_exports_enabled.rb +2 -4
- data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/ssl_certificates_over_thirty_days_from_expiry.rb +67 -34
- data/lib/neetob/cli/monthly_audit/misc/main.rb +1 -1
- data/lib/neetob/cli/monthly_audit/misc/sparkpost_sub_account_used_for_all_apps.rb +24 -18
- data/lib/neetob/cli/monthly_audit/security/code/active_record_doctor.rb +2 -2
- data/lib/neetob/cli/monthly_audit/security/code/brakeman.rb +7 -4
- data/lib/neetob/cli/monthly_audit/security/code/bundle_audit.rb +13 -4
- data/lib/neetob/cli/monthly_audit/security/code/fasterer.rb +2 -2
- data/lib/neetob/cli/monthly_audit/security/code/yarn_audit.rb +1 -1
- data/lib/neetob/cli/monthly_audit/security/github/dependabot_prs_merged.rb +20 -5
- data/lib/neetob/cli/monthly_audit/security/github/dependabot_turned_on.rb +25 -21
- data/lib/neetob/cli/neeto_deploy/autoscaling_config.rb +1 -1
- data/lib/neetob/cli/neeto_deploy/certificates.rb +1 -1
- data/lib/neetob/cli/neeto_deploy/commands.rb +7 -0
- data/lib/neetob/cli/neeto_deploy/config_vars/list.rb +1 -1
- data/lib/neetob/cli/neeto_deploy/config_vars/remove.rb +1 -1
- data/lib/neetob/cli/neeto_deploy/config_vars/upsert.rb +1 -1
- data/lib/neetob/cli/neeto_deploy/scheduled_exports.rb +1 -1
- data/lib/neetob/cli/neeto_deploy/unique_email_domains.rb +165 -0
- data/lib/neetob/cli/sre/base.rb +13 -13
- data/lib/neetob/cli/sre/check_essential_env.rb +7 -2
- data/lib/neetob/cli/sre/checklist.rb +2 -2
- data/lib/neetob/version.rb +1 -1
- data/neetob.gemspec +1 -1
- data/package.json +30 -0
- data/playwright.config.ts +39 -0
- data/scripts/config/.env.local +17 -0
- data/scripts/constants/auditData.ts +402 -0
- data/scripts/constants/routes.ts +30 -0
- data/scripts/constants/selectors.ts +4 -0
- data/scripts/constants/table.ts +30 -0
- data/scripts/constants/texts.ts +46 -0
- data/scripts/constants/userAgents.ts +14 -0
- data/scripts/utils/markdown.ts +23 -0
- data/scripts/workflows/dependabot.ts +104 -0
- data/scripts/workflows/honeybadger.ts +169 -0
- data/scripts/workflows/sparkpost.ts +204 -0
- data/tsconfig.json +35 -0
- data/yarn.lock +2216 -0
- metadata +24 -6
- 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
|
|
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.
|
|
16
|
+
ui.success "### 3.2.7. Checking whether Automatic HTTPS rewrites is enabled"
|
|
16
17
|
|
|
17
|
-
domains_data = [["Domain", "
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
audit_passed =
|
|
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
|
|
29
|
-
description: "
|
|
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
|
-
|
|
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 "
|
|
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
|
-
|
|
31
|
+
AutomaticHttpsRewritesIsEnabled.new.run
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
end
|
data/lib/neetob/cli/monthly_audit/instances_and_addons/honeybadger/setup_correctly_for_apps.rb
CHANGED
|
@@ -11,36 +11,35 @@ module Neetob
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def run
|
|
14
|
-
ui.success "### 3.4.1.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
ui.
|
|
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
|
|
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.
|
|
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
|
-
|
|
48
|
+
current_app_flagged_certificates = certificates_status.select { |certificate|
|
|
46
49
|
certificate["expires_before_30_days"] &&
|
|
47
|
-
|
|
48
|
-
|
|
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 =
|
|
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
|
-
|
|
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 #{
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
95
|
-
|
|
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.
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
ui.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|