puppet-validator 0.0.9 → 0.1.0

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 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