-
-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathenvironment_validator.rb
More file actions
172 lines (145 loc) · 5.45 KB
/
environment_validator.rb
File metadata and controls
172 lines (145 loc) · 5.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# frozen_string_literal: true
module Html2rss
module Web
##
# Environment validation for html2rss-web
# Handles validation of environment variables and configuration
module EnvironmentValidator # rubocop:disable Metrics/ModuleLength
# rubocop:disable Metrics/ClassLength
class << self
##
# Validate required environment variables on startup
# @return [void]
def validate_environment!
return if ENV['HTML2RSS_SECRET_KEY']
if non_production?
set_development_key
else
show_production_error
end
end
##
# Validate production security configuration
# @return [void]
def validate_production_security!
return if non_production?
validate_secret_key!
validate_account_configuration!
validate_build_metadata!
end
# @return [Boolean]
def development? = ENV['RACK_ENV'] == 'development'
# @return [Boolean]
def test? = ENV['RACK_ENV'] == 'test'
# @return [Boolean]
def non_production?
development? || test?
end
# @return [Boolean]
def auto_source_enabled?
Flags.auto_source_enabled?
end
private
def set_development_key
ENV['HTML2RSS_SECRET_KEY'] = 'development-default-key-not-for-production'
log_development_default_secret_key_warning
warn_lines(
'WARNING: Using default secret key for development/testing only!',
'Set HTML2RSS_SECRET_KEY environment variable for production use.'
)
nil
end
def show_production_error
SecurityLogger.log_config_validation_failure('secret_key', 'Missing required secret key')
warn_lines(*production_error_message.lines(chomp: true))
exit 1
end
def production_error_message
<<~ERROR
❌ ERROR: HTML2RSS_SECRET_KEY environment variable is not set!
This application is designed to be used via Docker Compose only.
Please read the project's README.md for setup instructions.
To generate a secure secret key and start the application:
1. Generate a secret key: openssl rand -hex 32
2. Edit docker-compose.yml and replace 'your-generated-secret-key-here' with your key
3. Start with: docker-compose up
For more information, see: https://github.com/html2rss/html2rss-web#configuration
ERROR
end
def validate_secret_key!
secret = ENV.fetch('HTML2RSS_SECRET_KEY', nil)
return unless secret == 'your-generated-secret-key-here' || secret.length < 32
SecurityLogger.log_config_validation_failure('secret_key', 'Invalid or weak secret key')
warn_lines(
'CRITICAL: Invalid secret key for production deployment!',
'Secret key must be at least 32 characters and not the default placeholder.',
'Generate a secure key: openssl rand -hex 32'
)
exit 1
end
# @return [void]
def validate_build_metadata!
return unless missing_build_metadata?
log_missing_build_metadata!
warn_lines(*missing_build_metadata_warning_lines)
exit 1
end
def validate_account_configuration!
accounts = AccountManager.accounts
weak_tokens = accounts.select { |acc| acc[:token].length < 16 }
return unless weak_tokens.any?
handle_weak_account_tokens!(weak_tokens)
end
# @param lines [Array<String>]
# @return [void]
def warn_lines(*lines)
lines.each { |line| Kernel.warn(line) }
nil
end
# @return [void]
def log_development_default_secret_key_warning
SecurityLogger.log_config_validation_failure(
'secret_key',
'Using development default secret key',
severity: :warn
)
end
# @param weak_tokens [Array<Hash{Symbol=>String}>]
# @return [void]
def handle_weak_account_tokens!(weak_tokens)
weak_usernames = weak_tokens.map { |acc| acc[:username] }.join(', ')
SecurityLogger.log_config_validation_failure('account_tokens', "Weak tokens for users: #{weak_usernames}")
warn_lines(
'CRITICAL: Weak authentication tokens detected in production!',
'All tokens must be at least 16 characters long.',
"Weak tokens found for users: #{weak_usernames}"
)
exit 1
end
# @return [Boolean]
def missing_build_metadata?
build_metadata_values.any?(&:empty?)
end
# @return [Array<String>]
def build_metadata_values
%w[BUILD_TAG GIT_SHA].map { |key| ENV.fetch(key, '').strip }
end
# @return [void]
def log_missing_build_metadata!
SecurityLogger.log_config_validation_failure(
'build_metadata',
'Missing BUILD_TAG or GIT_SHA'
)
end
# @return [Array<String>]
def missing_build_metadata_warning_lines
[
'CRITICAL: Missing build metadata for production deployment!',
'Set BUILD_TAG to the release build tag and GIT_SHA to the deployed commit SHA.'
]
end
end
# rubocop:enable Metrics/ClassLength
end
end
end