wunderbar 0.10.9 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -136,24 +136,28 @@ Basic interface
136
136
  A typical main program produces one or more of HTML, JSON, or plain text
137
137
  output. This is accomplished by providing one or more of the following:
138
138
 
139
- Wunderbar.html do
139
+ _html do
140
140
  code
141
141
  end
142
142
 
143
- Wunderbar.xhtml do
143
+ _xhtml do
144
144
  code
145
145
  end
146
146
 
147
- Wunderbar.json do
147
+ _json do
148
148
  code
149
149
  end
150
150
 
151
- Wunderbar.text do
151
+ _text do
152
152
  code
153
153
  end
154
154
 
155
- Arbitrary Ruby code can be placed in each. To append to the output produced,
156
- use the `_` methods described here.
155
+ Arbitrary Ruby code can be placed in each. Form parameters are made available
156
+ as instance variables (e.g., `@name`). Host environment (CGI, Rack, Sinatra)
157
+ values are accessible as methods of the `_` object: for example `_.headers`
158
+ (CGI), `_.set_cookie` (Rack), `_.redirect` (Sinatra).
159
+
160
+ To append to the output produced, use the `_` methods described below.
157
161
 
158
162
  Methods provided to Wunderbar.html
159
163
  ---
@@ -182,16 +186,33 @@ number of other convenience methods are defined:
182
186
  * `_.post?` -- was this invoked via HTTP POST?
183
187
  * `_.system` -- invokes a shell command, captures stdin, stdout, and stderr
184
188
  * `_.submit` -- runs command (or block) as a deamon process
189
+ * `_.xhtml?` -- output as XHTML?
185
190
 
186
191
  Access to all of the builder _defined_ methods (typically these end in an esclamation mark) and all of the Wunderbar module methods can be accessed in this way. Examples:
187
192
 
188
- * `_.tag! :foo`
189
- * `_.error 'Log message'`
193
+ * `_.tag! :foo`: insert elements where the name can be dynamic
194
+ * `_.error 'Log message'`: write a message to the server log
195
+ * `_import!`: insert markup with indentation matching the current output
190
196
 
191
197
  XHTML differs from HTML in the escaping of inline style and script elements.
192
198
  XHTML will also fall back to HTML output unless the user agent indicates it
193
199
  supports XHTML via the HTTP Accept header.
194
200
 
201
+ In addition to the default processing of elements, text, and attributes,
202
+ Wunderdar defines additional processing for the following:
203
+
204
+ * `_head`: insert meta charset utf-8
205
+ * `_svg`: insert svg namespace
206
+ * `_math`: insert math namespace
207
+ * `_coffeescript`: convert [coffeescript](http://coffeescript.org/) to JS and insert script tag
208
+
209
+ Note that adding an exclamation mark to the end of the tag name disables this
210
+ behavior.
211
+
212
+ If one of the attributes passed on the `_html` declaration is `:_width`, an
213
+ attempt will be made to reflow text in order to not exceed this line width.
214
+ This won't be done if it will affect what actually is displayed.
215
+
195
216
  Methods provided to Wunderbar.json
196
217
  ---
197
218
 
@@ -258,6 +279,20 @@ output stream, which provides access to other useful methods, for example:
258
279
  _.print 'foo'
259
280
  _.printf "Hello %s!\n", 'world'
260
281
 
282
+ Secure by default
283
+ ---
284
+
285
+ Wunderbar will properly escape all HTML and JSON output, eliminating problems
286
+ of HTML or JavaScript injection.
287
+
288
+ Unless you call `Wunderbar.unsafe!` at the top of your script, Wunderbar will
289
+ also set
290
+ [`$SAFE=1`](http://www.ruby-doc.org/docs/ProgrammingRuby/html/taint.html)
291
+ before processing requests. This means that you will need to
292
+ [`untaint`](ruby-doc.org/core/Object.html#method-i-untaint) all inputs
293
+ received from external sources before you make system calls or access the file
294
+ system.
295
+
261
296
  Globals provided
262
297
  ---
263
298
  * `$USER` - Host user id
@@ -276,18 +311,6 @@ Also, the following environment variables are set if they aren't already:
276
311
  Finally, the (Ruby 1.9.x) default external and internal encodings are set to
277
312
  UTF-8. For Ruby 1.8, `$KCODE` is set to `U`
278
313
 
279
- HTML methods
280
- ---
281
- * `_head`: insert meta charset utf-8
282
- * `_svg`: insert svg namespace
283
- * `_math`: insert math namespace
284
- * `_coffeescript`: convert [coffeescript](http://coffeescript.org/) to JS and insert script tag
285
- * `_import!`: insert markup with indentation matching the current output
286
- * `xhtml?`: output as XHTML?
287
-
288
- Note that adding an exclamation mark to the end of the tag name disables this
289
- behavior.
290
-
291
314
  Builder extensions
292
315
  ---
293
316
  * `indented_text!`: matches text indentation to markup
@@ -218,7 +218,7 @@ module Wunderbar
218
218
 
219
219
  @_builder << data
220
220
  rescue LoadError
221
- @_builder << string
221
+ @_builder << data
222
222
  end
223
223
  end
224
224
 
@@ -230,11 +230,15 @@ module Wunderbar
230
230
  instance_variable_set "@#{key}", value if key =~ /^\w+$/
231
231
  end
232
232
  end
233
+
234
+ def get_binding
235
+ binding
236
+ end
233
237
  end
234
238
 
239
+ require 'stringio'
235
240
  class TextBuilder < BuilderBase
236
241
  def initialize(scope)
237
- require 'stringio'
238
242
  @_target = StringIO.new
239
243
  @_scope = scope
240
244
  end
@@ -1,3 +1,5 @@
1
+ require 'digest/md5'
2
+
1
3
  module Wunderbar
2
4
  module CGI
3
5
 
@@ -31,7 +33,6 @@ module Wunderbar
31
33
  # Conditionally provide output, based on ETAG
32
34
  def self.out?(scope, headers, &block)
33
35
  content = block.call
34
- require 'digest/md5'
35
36
  etag = Digest::MD5.hexdigest(content)
36
37
 
37
38
  if scope.env['HTTP_IF_NONE_MATCH'] == etag.inspect
@@ -5,10 +5,14 @@ module Wunderbar
5
5
  TEXT = ARGV.delete('--text')
6
6
  end
7
7
 
8
- module Untaint
9
- def untaint_if_match regexp
10
- self.untaint if regexp.match(self)
11
- end
8
+ @@unsafe = false
9
+
10
+ def self.unsafe!(mode=true)
11
+ @@unsafe=mode
12
+ end
13
+
14
+ def self.safe?
15
+ not @@unsafe
12
16
  end
13
17
 
14
18
  class Scope
@@ -27,6 +27,7 @@ class HtmlMarkup < Wunderbar::BuilderBase
27
27
  # default namespace
28
28
  args << {} if args.empty?
29
29
  args.first[:xmlns] ||= 'http://www.w3.org/1999/xhtml' if Hash === args.first
30
+ @_width = args.first.delete(:_width) if Hash === args.first
30
31
 
31
32
  @x.text! "\xEF\xBB\xBF"
32
33
  @x.declare! :DOCTYPE, :html
@@ -34,6 +35,32 @@ class HtmlMarkup < Wunderbar::BuilderBase
34
35
  set_variables_from_params
35
36
  instance_eval(&block)
36
37
  end
38
+
39
+ if @_width
40
+ # reflow long lines
41
+ source = @x.target!.slice!(0..-1)
42
+ indent = col = 0
43
+ breakable = true
44
+ while not source.empty?
45
+ token = source.shift
46
+ indent = token[/^ */].length if col == 0
47
+ breakable = false if token.start_with? '<'
48
+ while token.length + col > @_width and breakable
49
+ break if token[0...-1].include? "\n"
50
+ split = token.rindex(' ', [@_width-col,0].max) || token.index(' ')
51
+ break unless split
52
+ break if col+split < indent+@_width/2
53
+ @x.target! << token[0...split] << "\n" << (' '*indent)
54
+ col = indent
55
+ token = token[split+1..-1]
56
+ end
57
+ breakable = true if token.end_with? '>'
58
+ @x.target! << token
59
+ col += token.length
60
+ col = 0 if token.end_with? "\n"
61
+ end
62
+ end
63
+
37
64
  @x.target!.join
38
65
  end
39
66
 
@@ -6,7 +6,11 @@ module Wunderbar
6
6
  @_request = Rack::Request.new(env)
7
7
  @_response = Rack::Response.new
8
8
  Wunderbar.logger = @_request.logger
9
- Wunderbar::CGI.call(self)
9
+ if Wunderbar.safe? and $SAFE==0
10
+ Proc.new { $SAFE=1; Wunderbar::CGI.call(self) }.call
11
+ else
12
+ Wunderbar::CGI.call(self)
13
+ end
10
14
  @_response.finish
11
15
  end
12
16
 
@@ -0,0 +1,46 @@
1
+ require 'active_support/core_ext'
2
+
3
+ module Wunderbar
4
+ module Rails
5
+ class HtmlHandler
6
+ cattr_accessor :default_format
7
+ self.default_format = Mime::HTML
8
+
9
+ def self.call(template)
10
+ pre = %{
11
+ x = HtmlMarkup.new(self);
12
+ instance_variables.each do |var|
13
+ x.instance_variable_set var, instance_variable_get(var)
14
+ end
15
+ }.strip.gsub(/\s+/, ' ')
16
+
17
+ post ="x._.target!.join"
18
+
19
+ # take care to preserve line numbers in original source
20
+ "#{pre}; x.instance_eval { #{template.source} }; #{post}"
21
+ end
22
+ end
23
+
24
+ class JsonHandler
25
+ cattr_accessor :default_format
26
+ self.default_format = Mime::JSON
27
+
28
+ def self.call(template)
29
+ pre = %{
30
+ x = Wunderbar::JsonBuilder.new(self);
31
+ instance_variables.each do |var|
32
+ x.instance_variable_set var, instance_variable_get(var)
33
+ end
34
+ }.strip.gsub(/\s+/, ' ')
35
+
36
+ post ="x.target!"
37
+
38
+ # take care to preserve line numbers in original source
39
+ "#{pre}; x.instance_eval { #{template.source} }; #{post}"
40
+ end
41
+ end
42
+
43
+ ActionView::Template.register_template_handler :_html, HtmlHandler
44
+ ActionView::Template.register_template_handler :_json, JsonHandler
45
+ end
46
+ end
@@ -35,6 +35,10 @@ elsif defined? Sinatra
35
35
 
36
36
  require 'wunderbar/sinatra'
37
37
 
38
+ elsif defined? ActionView::Template
39
+
40
+ require 'wunderbar/rails'
41
+
38
42
  else
39
43
 
40
44
  require 'etc'
@@ -79,7 +83,11 @@ else
79
83
  ENV['REQUEST_METHOD'] ||= 'GET' if ARGV.delete('--get')
80
84
 
81
85
  # CGI or command line
82
- Wunderbar::CGI.call(cgi)
86
+ if Wunderbar.safe? and $SAFE==0
87
+ Proc.new { $SAFE=1; Wunderbar::CGI.call(cgi) }.call
88
+ else
89
+ Wunderbar::CGI.call(cgi)
90
+ end
83
91
  end
84
92
  end
85
93
  end
@@ -1,5 +1,6 @@
1
1
  require 'sinatra'
2
2
  require 'digest/md5'
3
+ require 'nokogiri'
3
4
 
4
5
  module Wunderbar
5
6
  # Tilt template implementation
@@ -48,10 +49,24 @@ module Wunderbar
48
49
  instance_variable_set "@#{key}", value if key =~ /^[a-z]\w+$/
49
50
  end
50
51
  end
51
- if block
52
+
53
+ if not block
54
+ builder.instance_eval(data.untaint, eval_file)
55
+ elsif not data
52
56
  builder.instance_eval(&block)
53
- elsif data
54
- builder.instance_eval(data, eval_file)
57
+ else
58
+ context = builder.get_binding do
59
+ builder.instance_eval {_import! block.call}
60
+ end
61
+ context.eval(data.untaint, eval_file)
62
+ end
63
+ end
64
+
65
+ def _evaluate_safely(*args, &block)
66
+ if Wunderbar.safe? and $SAFE==0
67
+ Proc.new { $SAFE=1; _evaluate(*args, &block) }.call
68
+ else
69
+ _evaluate(*args, &block)
55
70
  end
56
71
  end
57
72
  end
@@ -62,7 +77,7 @@ module Wunderbar
62
77
  def evaluate(scope, locals, &block)
63
78
  builder = HtmlMarkup.new(scope)
64
79
  begin
65
- _evaluate(builder, scope, locals, &block)
80
+ _evaluate_safely(builder, scope, locals, &block)
66
81
  rescue Exception => exception
67
82
  scope.response.status = 500
68
83
  builder.clear!
@@ -90,7 +105,7 @@ module Wunderbar
90
105
  def evaluate(scope, locals, &block)
91
106
  builder = JsonBuilder.new(scope)
92
107
  begin
93
- _evaluate(builder, scope, locals, &block)
108
+ _evaluate_safely(builder, scope, locals, &block)
94
109
  rescue Exception => exception
95
110
  scope.content_type self.class.default_mime_type, :charset => 'utf-8'
96
111
  scope.response.status = 500
@@ -106,7 +121,7 @@ module Wunderbar
106
121
  def evaluate(scope, locals, &block)
107
122
  builder = TextBuilder.new(scope)
108
123
  begin
109
- _evaluate(builder, scope, locals, &block)
124
+ _evaluate_safely(builder, scope, locals, &block)
110
125
  scope.response.status = 404 if builder.target!.empty?
111
126
  rescue Exception => exception
112
127
  scope.headers['Content-Type'] = self.class.default_mime_type
@@ -1,8 +1,8 @@
1
1
  module Wunderbar
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 10
5
- TINY = 9
4
+ MINOR = 11
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
data/wunderbar.gemspec CHANGED
@@ -1,19 +1,23 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  Gem::Specification.new do |s|
4
- s.name = "wunderbar"
5
- s.version = "0.10.9"
4
+ s.name = %q{wunderbar}
5
+ s.version = "0.11.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
- s.authors = ["Sam Ruby"]
9
- s.date = "2012-04-18"
10
- s.description = " Wunderbar makes it easy to produce valid HTML5, wellformed XHTML, Unicode\n (utf-8), consistently indented, readable applications. This includes\n output that conforms to the Polyglot specification and the emerging\n results from the XML Error Recovery Community Group.\n"
11
- s.email = "rubys@intertwingly.net"
12
- s.files = ["wunderbar.gemspec", "README.md", "COPYING", "lib/wunderbar.rb", "lib/wunderbar", "lib/wunderbar/installation.rb", "lib/wunderbar/html-methods.rb", "lib/wunderbar/job-control.rb", "lib/wunderbar/server.rb", "lib/wunderbar/logger.rb", "lib/wunderbar/rack.rb", "lib/wunderbar/builder.rb", "lib/wunderbar/sinatra.rb", "lib/wunderbar/environment.rb", "lib/wunderbar/cgi-methods.rb", "lib/wunderbar/cssproxy.rb", "lib/wunderbar/version.rb"]
13
- s.homepage = "http://github.com/rubys/wunderbar"
14
- s.require_paths = ["lib"]
15
- s.rubygems_version = "1.8.21"
16
- s.summary = "HTML Generator and CGI application support"
8
+ s.authors = [%q{Sam Ruby}]
9
+ s.date = %q{2012-04-23}
10
+ s.description = %q{ Wunderbar makes it easy to produce valid HTML5, wellformed XHTML, Unicode
11
+ (utf-8), consistently indented, readable applications. This includes
12
+ output that conforms to the Polyglot specification and the emerging
13
+ results from the XML Error Recovery Community Group.
14
+ }
15
+ s.email = %q{rubys@intertwingly.net}
16
+ s.files = [%q{wunderbar.gemspec}, %q{README.md}, %q{COPYING}, %q{lib/wunderbar.rb}, %q{lib/wunderbar}, %q{lib/wunderbar/version.rb}, %q{lib/wunderbar/installation.rb}, %q{lib/wunderbar/cssproxy.rb}, %q{lib/wunderbar/rack.rb}, %q{lib/wunderbar/logger.rb}, %q{lib/wunderbar/builder.rb}, %q{lib/wunderbar/sinatra.rb}, %q{lib/wunderbar/rails.rb}, %q{lib/wunderbar/server.rb}, %q{lib/wunderbar/html-methods.rb}, %q{lib/wunderbar/job-control.rb}, %q{lib/wunderbar/environment.rb}, %q{lib/wunderbar/cgi-methods.rb}]
17
+ s.homepage = %q{http://github.com/rubys/wunderbar}
18
+ s.require_paths = [%q{lib}]
19
+ s.rubygems_version = %q{1.8.5}
20
+ s.summary = %q{HTML Generator and CGI application support}
17
21
 
18
22
  if s.respond_to? :specification_version then
19
23
  s.specification_version = 3
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wunderbar
3
3
  version: !ruby/object:Gem::Version
4
- hash: 37
4
+ hash: 51
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 10
9
- - 9
10
- version: 0.10.9
8
+ - 11
9
+ - 0
10
+ version: 0.11.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Sam Ruby
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-04-18 00:00:00 Z
18
+ date: 2012-04-23 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: builder
@@ -59,18 +59,19 @@ files:
59
59
  - README.md
60
60
  - COPYING
61
61
  - lib/wunderbar.rb
62
+ - lib/wunderbar/version.rb
62
63
  - lib/wunderbar/installation.rb
63
- - lib/wunderbar/html-methods.rb
64
- - lib/wunderbar/job-control.rb
65
- - lib/wunderbar/server.rb
66
- - lib/wunderbar/logger.rb
64
+ - lib/wunderbar/cssproxy.rb
67
65
  - lib/wunderbar/rack.rb
66
+ - lib/wunderbar/logger.rb
68
67
  - lib/wunderbar/builder.rb
69
68
  - lib/wunderbar/sinatra.rb
69
+ - lib/wunderbar/rails.rb
70
+ - lib/wunderbar/server.rb
71
+ - lib/wunderbar/html-methods.rb
72
+ - lib/wunderbar/job-control.rb
70
73
  - lib/wunderbar/environment.rb
71
74
  - lib/wunderbar/cgi-methods.rb
72
- - lib/wunderbar/cssproxy.rb
73
- - lib/wunderbar/version.rb
74
75
  homepage: http://github.com/rubys/wunderbar
75
76
  licenses: []
76
77
 
@@ -100,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
101
  requirements: []
101
102
 
102
103
  rubyforge_project:
103
- rubygems_version: 1.8.21
104
+ rubygems_version: 1.8.5
104
105
  signing_key:
105
106
  specification_version: 3
106
107
  summary: HTML Generator and CGI application support