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.
- 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 +67 -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 +40 -0
- 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/commands.rb +2 -1
- data/lib/neetob/cli/monthly_audit/databases/users_unique_email_index.rb +6 -1
- data/lib/neetob/cli/monthly_audit/databases/uuid_primary_key.rb +8 -0
- data/lib/neetob/cli/monthly_audit/github_issue_creation.rb +75 -0
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/always_use_https_is_enabled.rb +11 -0
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/automatic_https_rewrites_is_enabled.rb +43 -0
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/dns_entry_has_proxy_status.rb +9 -0
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/main.rb +2 -2
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/minimum_tls_version_is_one_point_two.rb +11 -0
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/spf_records_are_valid.rb +9 -0
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/ssl_tls_encryption_mode_set_to_full.rb +9 -0
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cronitor/setup_correctly_for_apps.rb +10 -0
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cronitor/setup_correctly_for_help_center.rb +12 -0
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cronitor/setup_correctly_for_landing_pages.rb +15 -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 +11 -17
- data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/essential_environment_variables_set.rb +7 -10
- 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 +8 -4
- data/lib/neetob/cli/monthly_audit/instances_and_addons/neeto_deploy_or_heroku/ssl_certificates_over_thirty_days_from_expiry.rb +69 -24
- data/lib/neetob/cli/monthly_audit/misc/main.rb +1 -1
- data/lib/neetob/cli/monthly_audit/misc/redirections_working_correctly.rb +14 -1
- data/lib/neetob/cli/monthly_audit/misc/sparkpost_sub_account_used_for_all_apps.rb +24 -18
- data/lib/neetob/cli/monthly_audit/perform.rb +7 -2
- data/lib/neetob/cli/monthly_audit/security/code/active_record_doctor.rb +10 -5
- data/lib/neetob/cli/monthly_audit/security/code/brakeman.rb +10 -2
- data/lib/neetob/cli/monthly_audit/security/code/bundle_audit.rb +19 -6
- data/lib/neetob/cli/monthly_audit/security/code/checks_for_unused_assets.rb +5 -0
- data/lib/neetob/cli/monthly_audit/security/code/fasterer.rb +10 -2
- data/lib/neetob/cli/monthly_audit/security/code/yarn_audit.rb +6 -1
- data/lib/neetob/cli/monthly_audit/security/github/dependabot_prs_merged.rb +20 -0
- 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 +26 -6
- data/lib/neetob/cli/monthly_audit/instances_and_addons/cloudflare/bot_protection_enabled.rb +0 -32
- 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
|
-
|
|
48
|
+
current_app_flagged_certificates = certificates_status.select { |certificate|
|
|
44
49
|
certificate["expires_before_30_days"] &&
|
|
45
|
-
|
|
46
|
-
|
|
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 =
|
|
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
|
-
|
|
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 #{
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
|
|
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.
|
|
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
|
-
{
|
|
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
|
-
|
|
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
|
|
@@ -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.
|
|
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
|
-
|
|
24
|
+
audit_passed = "Yes"
|
|
25
25
|
comments = nil
|
|
26
26
|
else
|
|
27
27
|
issues_found = "Yes"
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
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
|
-
|
|
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
|
-
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
ui.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
@@ -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
|
|
@@ -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
|
|