puppet-validator 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- ODkwMThiYjc2M2RiYTAwYjQzZjdiOGExNzA5M2VhNDcyZDE1MGUxZA==
5
- data.tar.gz: !binary |-
6
- YmUxZGMwYmM3ZGE3MjI0ZjNmZDUxMDdlMDYyYTZjOWExNDg3MzUzMA==
2
+ SHA1:
3
+ metadata.gz: 42c7dd8c42d525d1d2b73688beeb01b3595477a1
4
+ data.tar.gz: d121433916a22680ae5692e59ece41a8f60c9eb4
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- ZDM1NTQ3MDc2NGFhNDBkNGUxOWVlZjE0YTA1NmEwMWQ0ZDU4Mzc1NmNkN2Iz
10
- MmYyNzkwZGY3MjU5MmExYjExNDA0ZjZkODk2ZjdlMTJmYzYxMzBjNTY4ZDll
11
- MWZiNTBlMWMxYTRjZjRjYzc4YWZlY2U4NGNiZTk5MmM0YWEzY2U=
12
- data.tar.gz: !binary |-
13
- M2E0NGIwZDQ3NzZmNzliOTE2N2QyMWFjNDYyZWFmY2VlNzU3NjBhYzRiMDIx
14
- N2E4ZmRmYzJiYWY0OTc4ZjA2OGVlNzBkYWEzYjY2MWY1NGZlM2RmNGY0NDFh
15
- YmVkMWEwYjNmYzdiYmY3ZjBkYTBmNTNkMTk1MmI1NjU4NGQ0YmY=
6
+ metadata.gz: d8a6b17cef56ab2112e0414d97cfc31e501dc64c6fa9a5f358417cedc0be2ea47b8bcc9ba951367b6aa67c083af72d9ab659c557b2b0ec2645ae5545f02d62cc
7
+ data.tar.gz: 077da604c15b3beb0cde4513b63f9e01cd4eef89c5b42f91e0135f1f2e582d13f30cbf1a8f1fbdd0fbf2078dfde51eaa97e78b099a26cef3f8471ed0a6cec471
data/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # Puppet Validator:
2
2
  ## Puppet Code Validation as a service
3
3
 
4
- Puppet Validator is a simple web service that accepts arbitrary code submissions and
5
- validates it the way `puppet parser validate` would. It can optionally also
6
- run `puppet-lint` checks on the code and display both results together.
4
+ Puppet Validator is a simple web service that accepts arbitrary code submissions
5
+ and validates it the way `puppet parser validate` and `puppet-lint` would. For
6
+ simple and self-contained manifests, it can also show you a relationship graph.
7
7
 
8
8
  Puppet Validator is completely themeable, albeit rather primitively.
9
9
 
10
+ See an example running on https://validate.puppet.com
11
+
10
12
  ### Usage:
11
13
 
12
14
  #### Running the service directly
@@ -22,25 +24,33 @@ and will serve content directly out of its installation directory. You can
22
24
  override and customize the web content by passing the `-t` or `--theme`
23
25
  command-line argument. See [Creating your own theme](#creating-your-own-theme) below.
24
26
 
27
+ This can load code from several popular paste services and can gist validated
28
+ code to https://gist.github.com. These gists include a `referer` link back so
29
+ the gisted code can be re-validated at any time. If you'd like the `referer`
30
+ check to work properly, make sure to run this with a valid SSL certificate.
31
+
25
32
  Options:
26
33
 
27
34
  -d, --debug Display or log debugging messages
28
- -l, --logfile [LOGFILE] Path to logfile. Defaults to no logging, or
29
- /var/log/puppet-validator if no filename is passed.
35
+ --disable DISABLED_CHECKS Lint checks to disable. Either comma-separated list or filename.
36
+ -l, --logfile [LOGFILE] Path to logfile. Defaults to no logging, or /var/log/puppet-validator if no filename is passed.
30
37
  -p, --port PORT Port to listen on. Defaults to 9000.
31
38
  -t, --theme THEMEDIR Path to the theme directory.
32
- -x, --csrf Protect from cross site request forgery. Requires code to be
33
- submitted for validation via the webpage.
34
- -g, --graph Generate relationship graphs from validated code. Requires
35
- `graphviz` to be installed.
36
-
39
+ -x, --csrf Protect from cross site request forgery. Requires code to be submitted for validation via the webpage.
40
+ -g, --graph Generate relationship graphs from validated code. Requires `graphviz` to be installed.
41
+ --ssl Run with SSL support. Autogenerates a self-signed certificates by default.
42
+ --ssl-cert FILE Specify the SSL certificate you'd like use use. Pair with --ssl-key.
43
+ --ssl-key FILE Specify the SSL key file you'd like use use. Pair with --ssl-cert.
44
+
37
45
  -h, --help Displays this help
38
46
 
47
+
39
48
  #### Integrating with Middleware
40
49
 
41
50
  If you plan to run this as a public service, then you may want to run it under
42
- middleware, such as Phusion Passenger, for performance and scalability. The
43
- specific implementation will depend on your choice of webserver and middleware.
51
+ middleware (such as Phusion Passenger, Puma, or Unicorn) for performance and
52
+ scalability. The specific implementation will depend on your choice of webserver
53
+ and middleware.
44
54
 
45
55
  To configure Puppet Validator on Apache and Passenger, you'll need to
46
56
  <a href="https://www.phusionpassenger.com/library/install/apache/install/oss/el7/">
@@ -81,7 +91,6 @@ require 'puppet-validator'
81
91
  logger = Logger.new('/var/log/puppet-validator')
82
92
  logger.level = Logger::WARN
83
93
 
84
- PuppetValidator.set :puppet_versions, Dir.glob('*').select {|f| File.symlink? f and File.readlink(f) == '.' }
85
94
  PuppetValidator.set :root, File.dirname(__FILE__)
86
95
  PuppetValidator.set :logger, logger
87
96
 
@@ -93,7 +102,6 @@ PuppetValidator.set :disabled_lint_checks, ['80chars']
93
102
  # Protect from cross site request forgery. With this set, code may be
94
103
  # submitted for validation by the website only.
95
104
  #
96
- # Note: This will currently break multiple version validation.
97
105
  PuppetValidator.set :csrf, false
98
106
 
99
107
  # Provide the option to generate relationship graphs from validated code.
@@ -114,17 +122,19 @@ command *will overwrite* existing files, but it will warn you before it does so.
114
122
  root@master:~ # cd /etc/puppet-validator/
115
123
  root@master:/etc/puppet-validator # puppet-validator init
116
124
  Initializing directory as new Puppet Validator theme...
117
- root@master:/etc/puppet-validator # tree
125
+ root@master:/etc/puppet-validator # tree -L 2
118
126
  .
119
127
  ├── LICENSE
120
128
  ├── README.md
121
129
  ├── config.ru
122
130
  ├── public
123
- │   ├── info.png
124
- │   ├── prism-default.css
125
- │   ├── prism.js
131
+ │   ├── font-awesome-4.7.0
132
+ │   ├── gist.png
133
+ │   ├── relationships.html
134
+ │   ├── scripts.js
126
135
  │   ├── styles.css
127
- │   └── testing.html
136
+ │   ├── testing.html
137
+ │   └── validation.js
128
138
  └── views
129
139
  ├── index.erb
130
140
  └── result.erb
@@ -132,7 +142,7 @@ command *will overwrite* existing files, but it will warn you before it does so.
132
142
  Once you've created your theme, you can start the Puppet Validator service using the `-t`
133
143
  or `--theme` command line arguments to tell Puppet Validator where to find your content.
134
144
 
135
- root@master:~ # puppet-validator -t /etc/puppet-validator/
145
+ root@master:~ # puppet-validator --theme /etc/puppet-validator/
136
146
 
137
147
  Alternatively, you can edit your webserver virtual host configuration to point
138
148
  to the *public* directory within your new theme, as in the example shown above.
@@ -171,93 +181,18 @@ PuppetValidator.set :disabled_lint_checks, '/etc/puppet-validator/disabled_check
171
181
 
172
182
  #### Validating code against multiple Puppet versions
173
183
 
174
- It is not very straightforward to load multiple versions of a library gem in Ruby.
175
- This makes it virtually impossible to validate multiple versions of the language
176
- directly in the tool. However, Passenger has allowed you to load different Ruby
177
- versions in different `Location` blocks since version 4.0 by loading separate
178
- threads for each.
179
-
180
- We can take advantage of that by configuring multiple Ruby environments using `rvm`
181
- or `rbenv` and installing different gemsets. A simple Puppet module to do this
182
- is included in the repository, with the caveat that it was designed to fully own
183
- a single-purpose VM and has so far only been tested on CentOS 7.
184
-
185
- If configuring manually, you'll need to create a gemset for each Puppet version
186
- you want to validate, with something like the following.
187
-
188
- root@master:~ # rvm install ruby-1.9.3-p551
189
- Searching for binary rubies, this might take some time.
190
- [...]
191
- root@master:~ # rvm use 1.9
192
- Using /usr/local/rvm/gems/ruby-1.9.3-p551
193
- root@master:~ # rvm gemset create puppet2.7.4
194
- ruby-1.9.3-p551 - #gemset created /usr/local/rvm/gems/ruby-1.9.3-p551@puppet2.7.4
195
- ruby-1.9.3-p551 - #generating puppet2.7.4 wrappers........
196
- root@master:~ # rvm gemset use puppet2.7.4
197
- Using ruby-1.9.3-p551 with gemset puppet2.7.4
198
- root@master:~ # gem install puppet -v 2.7.4
199
- [...]
200
- root@master:~ # gem install puppet-validator
201
- [...]
202
- root@master:~ # passenger-config --ruby-command
203
- passenger-config was invoked through the following Ruby interpreter:
204
- Command: /usr/local/rvm/gems/ruby-1.9.3-p551@puppet2.7.4/wrappers/ruby
205
- Version: ruby 1.9.3p551 (2014-11-13 revision 48407) [x86_64-linux]
206
- To use in Apache: PassengerRuby /usr/local/rvm/gems/ruby-1.9.3-p551@puppet2.7.4/wrappers/ruby
207
- To use in Nginx : passenger_ruby /usr/local/rvm/gems/ruby-1.9.3-p551@puppet2.7.4/wrappers/ruby
208
- To use with Standalone: /usr/local/rvm/gems/ruby-1.9.3-p551@puppet2.7.4/wrappers/ruby /usr/bin/passenger start
209
-
210
-
211
- ## Notes for RVM users
212
- Do you want to know which command to use for a different Ruby interpreter? 'rvm use' that Ruby interpreter, then re-run 'passenger-config about ruby-command'.
213
-
214
- Make a note of the `PassengerRuby` command for each gemset. You'll use it in the next step.
215
-
216
- You will need a `Location` block in your Apache `VirtualHost` for each versioned
217
- Puppet gemset you created above. The example file below shows blocks for three
218
- Puppet versions with the current version installed into the default directory.
219
-
220
- ``` Apache
221
- <VirtualHost *:80>
222
- ServerName vhost.example.com
223
- DocumentRoot "/var/www/puppet-validator/public"
224
-
225
- # The default root will validate against the current Puppet version
226
- <Directory "/var/www/puppet-validator/public">
227
- Options -MultiViews
228
- AllowOverride All
229
- Require all granted
230
- </Directory>
231
-
232
- Alias /2.7.4 /var/www/puppet-validator/2.7.4/public
233
- <Location /2.7.4>
234
- PassengerBaseURI /2.7.4
235
- PassengerAppRoot /var/www/puppet-validator/2.7.4
236
- PassengerRuby "/usr/local/rvm/gems/ruby-1.9.3-p551@puppet2.7.4/wrappers/ruby"
237
- </Location>
238
-
239
- Alias /3.6.2 /var/www/puppet-validator/3.6.2/public
240
- <Location /3.6.2>
241
- PassengerBaseURI /3.6.2
242
- PassengerAppRoot /var/www/puppet-validator/3.6.2
243
- PassengerRuby "/usr/local/rvm/gems/ruby-1.9.3-p551@puppet3.6.2/wrappers/ruby"
244
- </Location>
245
-
246
- ## Logging
247
- ErrorLog "/var/log/httpd/vhost.example.com_error.log"
248
- ServerSignature Off
249
- CustomLog "/var/log/httpd/vhost.example.com_access.log" combined
250
- </VirtualHost>
251
- ```
184
+ Puppet Validator runs a new process to validate each submission. This means that
185
+ it can lazy-load the requested Puppet version on demand. Simply `gem install` all
186
+ the versions you want and they'll be visible in the drop-down selector.
252
187
 
253
- There is one final trick. Passenger requires a unique filesystem location for its
254
- `AppRoot`. However, it will respect symlinks, so let's create one for each version:
188
+ # Installing a specific version
189
+ root@master:~ # gem install puppet -v 5.3.3
255
190
 
256
- root@master:~ # cd /var/www/puppet-validator
257
- root@master:~ # ln -s . 2.7.4
258
- root@master:~ # ln -s . 3.6.2
191
+ # Installing several versions at once
192
+ root@master:~ # gem install puppet:3.8.8 puppet:4.10.0 puppet:5.3.3
259
193
 
260
- Now restart Apache and you're all gravy.
194
+ If you use the `puppet_validator` module, simply specify the versions you want
195
+ as an array,
261
196
 
262
197
  #### Running standalone with `systemd`
263
198
 
@@ -12,9 +12,11 @@ options = {
12
12
  :root => gemroot,
13
13
  :csrf => false,
14
14
  :graph => false,
15
+ :spec => '/var/puppet-validator/spec'
15
16
  }
16
17
  logfile = $stderr
17
18
  loglevel = Logger::WARN
19
+ ssl_opts = {:verify_peer => false}
18
20
 
19
21
  optparse = OptionParser.new { |opts|
20
22
  opts.banner = "Usage : puppet-validator [-p <port>] [-l [logfile]] [-t <themedir>] [-d]
@@ -47,6 +49,10 @@ optparse = OptionParser.new { |opts|
47
49
  options[:root] = arg
48
50
  end
49
51
 
52
+ opts.on("-s SPECDIR", "--spec SPECDIR", "Path to the directory where spec tests are located.") do |arg|
53
+ options[:spec] = arg
54
+ end
55
+
50
56
  opts.on("-x", "--csrf", "Protect from cross site request forgery. Requires code to be submitted for validation via the webpage.") do
51
57
  options[:csrf] = true
52
58
  end
@@ -55,6 +61,18 @@ optparse = OptionParser.new { |opts|
55
61
  options[:graph] = true
56
62
  end
57
63
 
64
+ opts.on("--ssl", "Run with SSL support. Autogenerates a self-signed certificates by default.") do
65
+ options[:ssl] = true
66
+ end
67
+
68
+ opts.on("--ssl-cert FILE", "Specify the SSL certificate you'd like use use. Pair with --ssl-key.") do |arg|
69
+ ssl_opts[:cert_chain_file] = arg
70
+ end
71
+
72
+ opts.on("--ssl-key FILE", "Specify the SSL key file you'd like use use. Pair with --ssl-cert.") do |arg|
73
+ ssl_opts[:private_key_file] = arg
74
+ end
75
+
58
76
  opts.separator('')
59
77
 
60
78
  opts.on("-h", "--help", "Displays this help") do
@@ -84,5 +102,24 @@ else
84
102
  logger.level = loglevel
85
103
  options[:logger] = logger
86
104
 
87
- PuppetValidator.run! options
105
+ if ssl_opts[:cert_chain_file] and ssl_opts[:private_key_file]
106
+ options[:ssl] = true
107
+ end
108
+
109
+ # These options should either both be nil or both be Strings
110
+ unless ssl_opts[:cert_chain_file].class == ssl_opts[:private_key_file].class
111
+ raise 'You must specify both the certificate and key file!'
112
+ end
113
+
114
+ PuppetValidator.run!(options) do |server|
115
+ if options[:ssl]
116
+ if server.respond_to? 'ssl='
117
+ logger.info 'Enabling SSL support.'
118
+ server.ssl = true
119
+ server.ssl_options = ssl_opts
120
+ else
121
+ logger.warn "Please 'gem install thin' or run via an app server for SSL support."
122
+ end
123
+ end
124
+ end
88
125
  end
data/config.ru CHANGED
@@ -4,7 +4,6 @@ require 'puppet-validator'
4
4
  logger = Logger.new('/var/log/puppet-validator')
5
5
  logger.level = Logger::WARN
6
6
 
7
- PuppetValidator.set :puppet_versions, Dir.glob('*').select {|f| File.symlink? f and File.readlink(f) == '.' }
8
7
  PuppetValidator.set :root, File.dirname(__FILE__)
9
8
  PuppetValidator.set :logger, logger
10
9
 
@@ -16,7 +15,6 @@ PuppetValidator.set :disabled_lint_checks, ['80chars']
16
15
  # Protect from cross site request forgery. With this set, code may be
17
16
  # submitted for validation by the website only.
18
17
  #
19
- # Note: This will currently break multiple version validation.
20
18
  PuppetValidator.set :csrf, false
21
19
 
22
20
  # Provide the option to generate relationship graphs from validated code.
@@ -1,19 +1,18 @@
1
1
  require 'json'
2
2
  require 'logger'
3
3
  require 'sinatra/base'
4
- require 'puppet'
5
- require 'puppet/parser'
6
- require 'puppet-lint'
7
-
8
- require 'graphviz'
9
4
  require 'nokogiri'
10
5
  require 'cgi'
6
+ require 'uri'
7
+ require 'open-uri'
11
8
 
12
- # something like 3,000 lines of code
13
- MAXSIZE = 100000
14
- CONTEXT = 3
9
+ MAXSIZE = 100000 # something like 3,000 lines of code
10
+ CONTEXT = 3 # how many lines of code around an error should we highlight?
15
11
 
16
12
  class PuppetValidator < Sinatra::Base
13
+ require 'puppet-validator/validators'
14
+ require 'puppet-validator/helpers'
15
+
17
16
  set :logging, true
18
17
  set :strict, true
19
18
 
@@ -25,6 +24,7 @@ class PuppetValidator < Sinatra::Base
25
24
  if settings.csrf
26
25
  session[:csrf] ||= SecureRandom.hex(32)
27
26
  response.set_cookie 'authenticity_token', {
27
+ :path => '/',
28
28
  :value => session[:csrf],
29
29
  :expires => Time.now + (60 * 60 * 24),
30
30
  }
@@ -34,16 +34,6 @@ class PuppetValidator < Sinatra::Base
34
34
  def initialize(app=nil)
35
35
  super(app)
36
36
 
37
- Puppet.initialize_settings rescue nil
38
- Puppet.settings[:app_management] = true if Gem::Version.new(Puppet.version) >= Gem::Version.new('4.3.2')
39
-
40
- # set up the base environment
41
- Puppet.push_context(Puppet.base_context(Puppet.settings), 'Setup for Puppet Validator') rescue nil
42
-
43
- # disable as much disk access as possible
44
- Puppet::Node::Facts.indirection.terminus_class = :memory
45
- Puppet::Node.indirection.cache_class = nil
46
-
47
37
  # make sure that all the settings we expect are defined.
48
38
  [:disabled_lint_checks, :puppet_versions, :csrf].each do |name|
49
39
  next if settings.respond_to? name
@@ -68,175 +58,230 @@ class PuppetValidator < Sinatra::Base
68
58
  end
69
59
  end
70
60
 
71
- # put our supported versions in reverse semver order
72
- settings.puppet_versions = settings.puppet_versions.sort_by { |v| Gem::Version.new(v) }.reverse
61
+ # put all installed Puppet versions in reverse semver order
62
+ #settings.puppet_versions = settings.puppet_versions.sort_by { |v| Gem::Version.new(v) }.reverse
63
+ settings.puppet_versions = Gem::Specification.all.select {|g| g.name == 'puppet' }.collect {|g| g.version.to_s }
64
+
65
+ settings.logger.error "Please gem install one or more Puppet versions." if settings.puppet_versions.empty?
73
66
 
74
67
  end
75
68
 
76
69
  get '/' do
77
- @versions = [Puppet.version] + settings.puppet_versions
70
+ @versions = settings.puppet_versions
71
+ @disabled = settings.disabled_lint_checks
72
+ # loads lint into global namespace, but I don't see an alternative
73
+ @checks = PuppetValidator::Validators::Lint.all_checks
74
+
75
+ erb :index
76
+ end
77
+
78
+ get '/load/referer' do
79
+ redirect "/load/#{request.referer}"
80
+ end
81
+
82
+ get '/load/*' do
83
+ location = params[:splat].first.sub(/(https?:)\/\/?/, '\1//')
84
+ logger.info "Loading code from: #{location}"
85
+
86
+ uri = location.downcase.start_with?('http') ? URI.parse(location) : URI.parse("https://#{location}")
87
+
88
+ case uri.host
89
+ when 'gist.github.com'
90
+ path = uri.path.end_with?('/raw') ? uri : "#{uri}/raw"
91
+
92
+ when 'pastebin.com', 'hastebin.com'
93
+ path = uri.path.start_with?('/raw') ? uri : "#{uri.scheme}://#{uri.host}/raw#{uri.path}"
94
+
95
+ else
96
+ path = nil
97
+ logger.info "Unrecognized paste service: #{uri}"
98
+ end
99
+
100
+ @versions = settings.puppet_versions
78
101
  @disabled = settings.disabled_lint_checks
79
- @checks = puppet_lint_checks
102
+ # loads lint into global namespace, but I don't see an alternative
103
+ @checks = PuppetValidator::Validators::Lint.all_checks
104
+ @location = location
105
+
106
+ if path
107
+ @code = sanitize_code(open(path).read) rescue nil
108
+ end
80
109
 
81
110
  erb :index
82
111
  end
83
112
 
113
+ # The all-in-one blob that renders via an erb page
84
114
  post '/validate' do
85
115
  logger.info "Validating code from #{request.ip}."
86
116
  logger.debug "validating #{request.ip}: #{params['code']}"
87
117
 
88
- halt 403, 'Request validation failed.' unless safe?
89
-
90
- frag = Nokogiri::HTML.fragment(params['code'])
91
- unless frag.elements.empty?
92
- logger.warn 'HTML code found in validation string'
93
- frag.elements.each { |elem| logger.debug "HTML: #{elem.to_s}" }
94
- params['code'] = CGI.escapeHTML(params['code'])
118
+ if params.include? 'load'
119
+ redirect "/load/#{params['location']}"
95
120
  end
96
121
 
97
- if request.body.size <= MAXSIZE
98
- result = validate params['code']
99
- lint = lint(params['code'], params['checks']) if params['lint'] == 'on'
100
- lint ||= {} # but make sure we have a data object to iterate
122
+ validate_request!
123
+ sanitize_code! # make code safe for re-rendering
101
124
 
102
- @version = Puppet.version
103
- @code = params['code']
104
- @message = result[:message]
105
- @status = result[:status] ? :success : :fail
106
- @line = result[:line]
107
- @column = result[:pos]
108
- @lint_warnings = ! lint.empty?
125
+ PuppetValidator.run_in_process do
126
+ if params.include? 'relationships' and settings.graph
127
+ syntax = PuppetValidator::Validators::Syntax.new(settings)
128
+ results = syntax.validate(params['code'])
129
+ results[:status] ? syntax.render! : results[:message]
109
130
 
110
- # initial highlighting for the potential syntax error
111
- if @line
112
- start = [@line - CONTEXT, 1].max
113
- initial = {"#{start}-#{@line}" => nil}
114
131
  else
115
- initial = {}
132
+ @result = validate_all
133
+ @result[:code] = params['code']
134
+
135
+ erb :result
116
136
  end
137
+ end
138
+ end
117
139
 
118
- # then add all the lint warnings and tooltip
119
- @highlights = lint.inject(initial) do |acc, item|
120
- acc.merge({item[:line] => "#{item[:kind].upcase}: #{item[:message]}"})
121
- end.to_json
140
+ ################### API v0 endpoints ###################
122
141
 
123
- @relationships = rendered_dot(@code) if params['relationships'] == 'on'
124
- else
125
- @message = "Submitted code size is #{request.body.size}, which is larger than the maximum size of #{MAXSIZE}."
126
- @status = :fail
127
- logger.error @message
142
+ post '/api/v0/validate' do
143
+ validate_request!
144
+
145
+ PuppetValidator.run_in_process do
146
+ validate_all.to_json
147
+ end
148
+ end
149
+
150
+ post '/api/v0/validate/syntax' do
151
+ validate_request!
152
+
153
+ PuppetValidator.run_in_process do
154
+ syntax = PuppetValidator::Validators::Syntax.new(settings)
155
+ syntax.validate(params['code']).to_json
156
+ end
157
+ end
158
+
159
+ post '/api/v0/validate/relationships' do
160
+ validate_request!
161
+ halt 403, 'Graph generation disabled.' unless settings.graph
162
+
163
+ PuppetValidator.run_in_process do
164
+ syntax = PuppetValidator::Validators::Syntax.new(settings)
165
+
166
+ # need to prebuild the catalog first
167
+ results = syntax.validate(params['code'])
168
+ # return either an SVG or the error message
169
+ results[:status] ? syntax.render! : results[:message]
128
170
  end
129
171
 
130
- erb :result
131
172
  end
132
173
 
174
+ post '/api/v0/validate/lint' do
175
+ validate_request!
176
+
177
+ PuppetValidator.run_in_process do
178
+ linter = PuppetValidator::Validators::Lint.new(settings)
179
+ linter.validate(params['code']).to_json
180
+ end
181
+ end
182
+
183
+ post '/api/v0/validate/rspec' do
184
+ validate_request!
185
+
186
+ PuppetValidator.run_in_process do
187
+ rspec = PuppetValidator::Validators::Rspec.new(settings)
188
+ rspec.validate(params['code'], params['spec']).to_json
189
+ end
190
+ end
191
+
192
+ #######################################################
193
+
133
194
  not_found do
134
195
  halt 404, "You shall not pass! (page not found)\n"
135
196
  end
136
197
 
137
198
  helpers do
138
199
 
139
- def safe?
200
+ def validate_request!
201
+ csrf_safe!
202
+ check_size_limit!
203
+ end
204
+
205
+ def csrf_safe!
140
206
  return true unless settings.csrf
141
207
  if session[:csrf] == params['_csrf'] && session[:csrf] == request.cookies['authenticity_token']
142
208
  true
143
209
  else
144
- logger.warn 'CSRF attempt detected.'
145
- false
146
- end
147
- end
148
-
149
- def validate(data)
150
- begin
151
- Puppet[:code] = data
152
-
153
- if Puppet::Node::Environment.respond_to?(:create)
154
- validation_environment = Puppet::Node::Environment.create(:production, [])
155
- validation_environment.check_for_reparse
156
- else
157
- validation_environment = Puppet::Node::Environment.new(:production)
158
- end
159
-
160
- validation_environment.known_resource_types.clear
210
+ logger.warn 'CSRF attempt detected. Ensure that server time is correct.'
211
+ logger.debug "session: #{session[:csrf]}"
212
+ logger.debug " param: #{params['_csrf']}"
213
+ logger.debug " cookie: #{request.cookies['authenticity_token']}"
161
214
 
162
- {:status => true, :message => 'Syntax OK'}
163
- rescue => detail
164
- logger.warn detail.message
165
- err = {:status => false, :message => detail.message}
166
- err[:line] = detail.line if detail.methods.include? :line
167
- err[:pos] = detail.pos if detail.methods.include? :pos
168
- err
215
+ halt 403, 'Request validation failed.'
169
216
  end
170
217
  end
171
218
 
172
- def lint(data, checks=nil)
173
- begin
174
- if checks
175
- logger.info "Disabling checks: #{(puppet_lint_checks - checks).inspect}"
176
-
177
- checks.each do |check|
178
- PuppetLint.configuration.send("enable_#{check}")
179
- end
180
-
181
- (puppet_lint_checks - checks).each do |check|
182
- PuppetLint.configuration.send("disable_#{check}")
183
- end
184
- else
185
- logger.info "Disabling checks: #{settings.disabled_lint_checks.inspect}"
186
-
187
- settings.disabled_lint_checks.each do |check|
188
- PuppetLint.configuration.send("disable_#{check}")
189
- end
190
- end
219
+ def check_size_limit!
220
+ content = request.body.read
221
+ request.body.rewind
191
222
 
192
- linter = PuppetLint.new
193
- linter.code = data
194
- linter.run
195
- linter.print_problems
196
- rescue => detail
197
- logger.warn detail.message
198
- nil
223
+ if content.size > MAXSIZE
224
+ halt 403, "Submitted code size is #{content.size}, which is larger than the maximum size of #{MAXSIZE}."
199
225
  end
200
226
  end
201
227
 
202
- def puppet_lint_checks
203
- # sanitize because reasonss
204
- PuppetLint.configuration.checks.map {|check| check.to_s}
228
+ def sanitize_code!
229
+ params['code'] = sanitize_code(params['code'])
205
230
  end
206
231
 
207
- def rendered_dot(code)
208
- return unless settings.graph
209
-
210
- begin
211
- node = Puppet::Node.indirection.find('validator')
212
- catalog = Puppet::Resource::Catalog.indirection.find(node.name, :use_node => node)
213
-
214
- # These calls are failing due to an internal method not being available in 2 & 3.x. Suspect
215
- # that it's related to the compiler not being set up fully?
216
- catalog.remove_resource(catalog.resource("Stage", :main)) rescue nil
217
- catalog.remove_resource(catalog.resource("Class", :settings)) rescue nil
232
+ def sanitize_code(code)
233
+ frag = Nokogiri::HTML.fragment(code)
234
+ unless frag.elements.empty?
235
+ logger.warn 'HTML code found in validation string'
236
+ frag.elements.each { |elem| logger.debug "HTML: #{elem.to_s}" }
237
+ code = CGI.escapeHTML(code)
238
+ end
239
+ code
240
+ end
218
241
 
219
- graph = catalog.to_ral.relationship_graph.to_dot
242
+ def validate_all
243
+ syntax = PuppetValidator::Validators::Syntax.new(settings, params['version'])
244
+ result = syntax.validate(params['code'])
220
245
 
221
- svg = GraphViz.parse_string(graph) do |graph|
222
- graph[:label] = 'Resource Relationships'
246
+ # munge the data slightly to make it more consumable
247
+ result[:version] = params['version']
248
+ result[:success] = result[:status]
249
+ result[:status] = result[:status] ? :success : :fail
250
+ result[:column] = result[:pos]
251
+ result[:messages] = []
223
252
 
224
- graph.each_node do |name, node|
225
- next unless name.start_with? 'Whit'
226
- newname = name.dup
227
- newname.sub!('Admissible_class', 'Starting Class')
228
- newname.sub!('Completed_class', 'Finishing Class')
229
- node[:label] = newname[5..-2]
230
- end
231
- end.output(:svg => String)
253
+ # initial highlighting for the potential syntax error
254
+ if result[:line]
255
+ line = result[:line]
256
+ start = [line - CONTEXT, 0].max
257
+
258
+ result[:messages] << {
259
+ :from => [start, 0],
260
+ :to => [line - 1, result[:column]],
261
+ :message => result[:message],
262
+ :severity => 'error',
263
+ }
264
+ end
232
265
 
233
- rescue => detail
234
- logger.warn detail.message
235
- logger.debug detail.backtrace.join "\n"
236
- return { :status => false, :message => detail.message }
266
+ # then add all the lint warnings and tooltip
267
+ if params['lint']
268
+ linter = PuppetValidator::Validators::Lint.new(settings)
269
+ lint = linter.validate(params['code'], params['checks'])
270
+
271
+ result[:lint_warnings] = ! lint.empty?
272
+
273
+ lint.each do |item|
274
+ line = item[:line]-1;
275
+ result[:messages] << {
276
+ :from => [line, 0],
277
+ :to => [line, 1000],
278
+ :message => "#{item[:kind].upcase}: #{item[:message]}",
279
+ :severity => 'warning'
280
+ }
281
+ end
237
282
  end
238
283
 
239
- { :status => true, :data => svg }
284
+ result
240
285
  end
241
286
 
242
287
  end