mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-27 09:36:19 -05:00
added sane sanitizer (Google Cajole) that is much more robust than old one ... yay for smilies
added sane way to do $LAB includes - pattern to be expanded people keep on messing structure.sql
This commit is contained in:
parent
a1099ed74e
commit
0c085059c9
9 changed files with 2592 additions and 2080 deletions
2424
app/assets/javascripts/defer/html-sanitizer-bundle.js
Normal file
2424
app/assets/javascripts/defer/html-sanitizer-bundle.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,92 @@
|
|||
/* jQuery sanitizer for html, used by our previewer */
|
||||
// Sam: I wrote this but it is totally unsafe so I ported Google Cajole
|
||||
// Thing is Cajole is old and complex (albeit super duper fast)
|
||||
//
|
||||
// I would like this ported to: https://github.com/tautologistics/node-htmlparser , perf tested
|
||||
// and move off cajole
|
||||
//
|
||||
// See also: http://stackoverflow.com/questions/14971083/is-jquerys-safe-from-xss
|
||||
//
|
||||
|
||||
(function( $ ) {
|
||||
$.fn.sanitize = function() {
|
||||
$.find('*').each(function(){
|
||||
|
||||
});
|
||||
};
|
||||
})( jQuery );
|
||||
// (function( $ ) {
|
||||
//
|
||||
// var elements = ["a", "abbr", "aside", "b", "bdo", "blockquote", "br",
|
||||
// "caption", "cite", "code", "col", "colgroup", "dd", "div",
|
||||
// "del", "dfn", "dl", "dt", "em", "hr", "figcaption", "figure",
|
||||
// "h1", "h2", "h3", "h4", "h5", "h6", "hgroup", "i", "img", "ins",
|
||||
// "kbd", "li", "mark", "ol", "p", "pre", "q", "rp", "rt", "ruby",
|
||||
// "s", "samp", "small", "span", "strike", "strong", "sub", "sup",
|
||||
// "table", "tbody", "td", "tfoot", "th", "thead", "time", "tr", "u",
|
||||
// "ul", "var", "wbr"];
|
||||
//
|
||||
// var attributes = {
|
||||
// 'all' : ['dir', 'lang', 'title', 'class'],
|
||||
// 'aside' : ['data-post', 'data-full', 'data-topic'],
|
||||
// 'a' : ['href'],
|
||||
// 'blockquote' : ['cite'],
|
||||
// 'col' : ['span', 'width'],
|
||||
// 'colgroup' : ['span', 'width'],
|
||||
// 'del' : ['cite', 'datetime'],
|
||||
// 'img' : ['align', 'alt', 'height', 'src', 'width'],
|
||||
// 'ins' : ['cite', 'datetime'],
|
||||
// 'ol' : ['start', 'reversed', 'type'],
|
||||
// 'q' : ['cite'],
|
||||
// 'span' : ['style'],
|
||||
// 'table' : ['summary', 'width', 'style', 'cellpadding', 'cellspacing'],
|
||||
// 'td' : ['abbr', 'axis', 'colspan', 'rowspan', 'width', 'style'],
|
||||
// 'th' : ['abbr', 'axis', 'colspan', 'rowspan', 'scope', 'width', 'style'],
|
||||
// 'time' : ['datetime', 'pubdate'],
|
||||
// 'ul' : ['type']
|
||||
//
|
||||
// };
|
||||
//
|
||||
// var elementMap = {};
|
||||
// $.each(elements, function(idx,e){
|
||||
// elementMap[e] = true;
|
||||
// });
|
||||
//
|
||||
// var scrubAttributes = function(e){
|
||||
// $.each(e.attributes, function(idx, attr){
|
||||
//
|
||||
// if($.inArray(attr.name, attributes.all) === -1 &&
|
||||
// $.inArray(attr.name, attributes[e.tagName.toLowerCase()]) === -1) {
|
||||
// e.removeAttribute(attr.name);
|
||||
// }
|
||||
// });
|
||||
// return(e);
|
||||
// };
|
||||
//
|
||||
// var scrubNode = function(e){
|
||||
// if (!e.tagName) { return(e); }
|
||||
// if(elementMap[e.tagName.toLowerCase()]){
|
||||
// return scrubAttributes(e);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// var scrubTree = function(e) {
|
||||
// if (!e) { return; }
|
||||
//
|
||||
// var clean = scrubNode(e);
|
||||
// if(!clean){
|
||||
// e.parentNode.removeChild(e);
|
||||
// }
|
||||
// else {
|
||||
// $.each(clean.children, function(idx, inner){
|
||||
// scrubTree(inner);
|
||||
// });
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// $.fn.sanitize = function() {
|
||||
// clean = this.filter(function(){
|
||||
// return scrubNode(this);
|
||||
// }).each(function(){
|
||||
// scrubTree(this);
|
||||
// });
|
||||
//
|
||||
// return clean;
|
||||
// };
|
||||
// })( jQuery );
|
||||
|
|
|
@ -97,10 +97,7 @@ Discourse.Utilities =
|
|||
range.select()
|
||||
|
||||
markdownConverter: (opts)->
|
||||
if opts.sanitize
|
||||
converter = new Markdown.getSanitizingConverter()
|
||||
else
|
||||
converter = new Markdown.Converter()
|
||||
converter = new Markdown.Converter()
|
||||
|
||||
mentionLookup = opts.mentionLookup if opts
|
||||
mentionLookup = mentionLookup || Discourse.Mention.lookupCache
|
||||
|
@ -157,6 +154,12 @@ Discourse.Utilities =
|
|||
converter.hooks.chain "postConversion", (text) =>
|
||||
Discourse.BBCode.format(text, opts)
|
||||
|
||||
|
||||
if opts.sanitize
|
||||
converter.hooks.chain "postConversion", (text) =>
|
||||
return "" unless window.sanitizeHtml
|
||||
sanitizeHtml(text)
|
||||
|
||||
converter
|
||||
|
||||
|
||||
|
|
|
@ -60,11 +60,11 @@ window.Discourse.ComposerView = window.Discourse.View.extend
|
|||
fetchNewUserEducation: (->
|
||||
|
||||
# If creating a topic, use topic_count, otherwise post_count
|
||||
count = if @get('content.creatingTopic') then Discourse.get('currentUser.topic_count') else Discourse.get('currentUser.reply_count')
|
||||
count = if @get('content.creatingTopic') then Discourse.get('currentUser.topic_count') else Discourse.get('currentUser.reply_count')
|
||||
if (count >= Discourse.SiteSettings.educate_until_posts)
|
||||
@set('educationClosed', true)
|
||||
@set('educationContents', '')
|
||||
return
|
||||
return
|
||||
|
||||
return unless @get('controller.hasReply')
|
||||
|
||||
|
@ -106,7 +106,7 @@ window.Discourse.ComposerView = window.Discourse.View.extend
|
|||
$('#new-user-education').css('bottom', sizePx)
|
||||
).observes('content.composeState')
|
||||
|
||||
keyUp: (e) ->
|
||||
keyUp: (e) ->
|
||||
controller = @get('controller')
|
||||
controller.checkReplyLength()
|
||||
controller.hitEsc() if e.which == 27
|
||||
|
@ -139,6 +139,8 @@ window.Discourse.ComposerView = window.Discourse.View.extend
|
|||
@wmdInput = $wmdInput = $('#wmd-input')
|
||||
return if $wmdInput.length == 0 || $wmdInput.data('init') == true
|
||||
|
||||
$LAB.script(assetPath('defer/html-sanitizer-bundle'))
|
||||
|
||||
Discourse.ComposerView.trigger("initWmdEditor")
|
||||
|
||||
template = Handlebars.compile("<div class='autocomplete'>
|
||||
|
|
|
@ -1,25 +1,41 @@
|
|||
<%- if mini_profiler_enabled? %>
|
||||
<%- Rack::MiniProfiler.step "application" do %>
|
||||
<%= javascript_include_tag "application" %>
|
||||
<%-end%>
|
||||
|
||||
<%- Rack::MiniProfiler.step "admin" do %>
|
||||
<%= javascript_include_tag "admin"%>
|
||||
<%-end%>
|
||||
<%- else %>
|
||||
<%= javascript_include_tag "application" %>
|
||||
<%- if admin? %>
|
||||
<%= javascript_include_tag "admin"%>
|
||||
<%- end %>
|
||||
<%- end%>
|
||||
|
||||
<script>
|
||||
Discourse.CDN = '<%= Rails.configuration.action_controller.asset_host %>';
|
||||
Discourse.BaseUrl = '<%= RailsMultisite::ConnectionManagement.current_hostname %>';
|
||||
Discourse.Environment = '<%= Rails.env %>';
|
||||
window.Discourse.Router.map(function() {
|
||||
return Discourse.routeBuilder.call(this);
|
||||
});
|
||||
Discourse.start()
|
||||
Discourse.initialize()
|
||||
</script>
|
||||
<script>
|
||||
window.assetPath = (function(){
|
||||
|
||||
// TODO: automate this to grab from the manifest, Rails voodoo should be able to get it
|
||||
var map = {
|
||||
'defer/html-sanitizer-bundle': <%= asset_path('defer/html-sanitizer-bundle').inspect.html_safe %>
|
||||
};
|
||||
|
||||
var assetPath = function(asset){
|
||||
return map[asset];
|
||||
};
|
||||
|
||||
return assetPath;
|
||||
})();
|
||||
</script>
|
||||
|
||||
<%- if mini_profiler_enabled? %>
|
||||
<%- Rack::MiniProfiler.step "application" do %>
|
||||
<%= javascript_include_tag "application" %>
|
||||
<%-end%>
|
||||
|
||||
<%- Rack::MiniProfiler.step "admin" do %>
|
||||
<%= javascript_include_tag "admin"%>
|
||||
<%-end%>
|
||||
<%- else %>
|
||||
<%= javascript_include_tag "application" %>
|
||||
<%- if admin? %>
|
||||
<%= javascript_include_tag "admin"%>
|
||||
<%- end %>
|
||||
<%- end%>
|
||||
|
||||
<script>
|
||||
Discourse.CDN = '<%= Rails.configuration.action_controller.asset_host %>';
|
||||
Discourse.BaseUrl = '<%= RailsMultisite::ConnectionManagement.current_hostname %>';
|
||||
Discourse.Environment = '<%= Rails.env %>';
|
||||
window.Discourse.Router.map(function() {
|
||||
return Discourse.routeBuilder.call(this);
|
||||
});
|
||||
Discourse.start()
|
||||
Discourse.initialize()
|
||||
</script>
|
||||
|
|
|
@ -29,7 +29,9 @@ module Discourse
|
|||
|
||||
config.assets.paths += %W(#{config.root}/config/locales)
|
||||
|
||||
config.assets.precompile += ['admin.js', 'admin.css', 'shiny/shiny.css', 'preload_store.js', 'jquery.js']
|
||||
config.assets.precompile += [
|
||||
'admin.js', 'admin.css', 'shiny/shiny.css', 'preload_store.js', 'jquery.js', 'defer/html-sanitizer-bundle.js'
|
||||
]
|
||||
|
||||
# Activate observers that should always be running.
|
||||
config.active_record.observers = [
|
||||
|
|
2040
db/structure.sql
2040
db/structure.sql
File diff suppressed because it is too large
Load diff
19
spec/javascripts/sanitize_spec.js
Normal file
19
spec/javascripts/sanitize_spec.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
describe("sanitize", function(){
|
||||
|
||||
|
||||
it("strips all script tags", function(){
|
||||
sanitized = sanitizeHtml("<div><script>alert('hi');</script></div>");
|
||||
|
||||
expect(sanitized)
|
||||
.toBe("<div></div>");
|
||||
});
|
||||
|
||||
it("strips disallowed attributes", function(){
|
||||
sanitized = sanitizeHtml("<div><p class=\"funky\" wrong='1'>hello</p></div>");
|
||||
|
||||
expect(sanitized)
|
||||
.toBe("<div><p class=\"funky\">hello</p></div>");
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -40,6 +40,7 @@
|
|||
//= require_tree ../../app/assets/javascripts/discourse/templates
|
||||
//= require_tree ../../app/assets/javascripts/discourse/routes
|
||||
|
||||
//= require_tree ../../app/assets/javascripts/defer
|
||||
|
||||
//= require_tree .
|
||||
|
||||
|
|
Loading…
Reference in a new issue