Compare commits
	
		
			9 Commits
		
	
	
		
			abce2e03d1
			...
			6337350b60
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6337350b60 | |||
| 986acfac38 | |||
| b27149b5ba | |||
| eb1284060a | |||
| 4266ac1f12 | |||
| 6b4709d011 | |||
| ef8d3068a8 | |||
|   | a23c14389b | ||
|   | 6dfd8db0a6 | 
| @ -1,5 +1,5 @@ | |||||||
| from snek.system.service import BaseService | from snek.system.service import BaseService | ||||||
| from snek.system.template import whitelist_attributes | from snek.system.template import sanitize_html | ||||||
| import time | import time | ||||||
| 
 | 
 | ||||||
| class ChannelMessageService(BaseService): | class ChannelMessageService(BaseService): | ||||||
| @ -69,10 +69,10 @@ class ChannelMessageService(BaseService): | |||||||
|                 "color": user["color"], |                 "color": user["color"], | ||||||
|             } |             } | ||||||
|         ) |         ) | ||||||
|         context['message'] = whitelist_attributes(context['message']) |  | ||||||
|         try: |         try: | ||||||
|             template = self.app.jinja2_env.get_template("message.html") |             template = self.app.jinja2_env.get_template("message.html") | ||||||
|             model["html"] = template.render(**context) |             model["html"] = template.render(**context) | ||||||
|  |             model['html'] = sanitize_html(model['html']) | ||||||
|         except Exception as ex: |         except Exception as ex: | ||||||
|             print(ex, flush=True) |             print(ex, flush=True) | ||||||
| 
 | 
 | ||||||
| @ -118,7 +118,6 @@ class ChannelMessageService(BaseService): | |||||||
|     async def save(self, model): |     async def save(self, model): | ||||||
|         context = {} |         context = {} | ||||||
|         context.update(model.record) |         context.update(model.record) | ||||||
|         context['message'] = whitelist_attributes(context['message']) |  | ||||||
|         user = await self.app.services.user.get(model["user_uid"]) |         user = await self.app.services.user.get(model["user_uid"]) | ||||||
|         context.update( |         context.update( | ||||||
|             { |             { | ||||||
| @ -130,6 +129,7 @@ class ChannelMessageService(BaseService): | |||||||
|         ) |         ) | ||||||
|         template = self.app.jinja2_env.get_template("message.html") |         template = self.app.jinja2_env.get_template("message.html") | ||||||
|         model["html"] = template.render(**context) |         model["html"] = template.render(**context) | ||||||
|  |         model['html'] = sanitize_html(model['html']) | ||||||
|         return await super().save(model) |         return await super().save(model) | ||||||
| 
 | 
 | ||||||
|     async def offset(self, channel_uid, page=0, timestamp=None, page_size=30): |     async def offset(self, channel_uid, page=0, timestamp=None, page_size=30): | ||||||
|  | |||||||
| @ -3,8 +3,15 @@ export class EventHandler { | |||||||
|     this.subscribers = {}; |     this.subscribers = {}; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   addEventListener(type, handler) { |   addEventListener(type, handler, { once = false } = {}) { | ||||||
|     if (!this.subscribers[type]) this.subscribers[type] = []; |     if (!this.subscribers[type]) this.subscribers[type] = []; | ||||||
|  |     if (once) { | ||||||
|  |       const originalHandler = handler; | ||||||
|  |       handler = (...args) => { | ||||||
|  |         originalHandler(...args); | ||||||
|  |         this.removeEventListener(type, handler); | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|     this.subscribers[type].push(handler); |     this.subscribers[type].push(handler); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -12,4 +19,15 @@ export class EventHandler { | |||||||
|     if (this.subscribers[type]) |     if (this.subscribers[type]) | ||||||
|       this.subscribers[type].forEach((handler) => handler(...data)); |       this.subscribers[type].forEach((handler) => handler(...data)); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   removeEventListener(type, handler) { | ||||||
|  |     if (!this.subscribers[type]) return; | ||||||
|  |     this.subscribers[type] = this.subscribers[type].filter( | ||||||
|  |       (h) => h !== handler | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     if (this.subscribers[type].length === 0) { | ||||||
|  |       delete this.subscribers[type]; | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -57,17 +57,6 @@ export class ReplyEvent extends Event { | |||||||
| class MessageElement extends HTMLElement { | class MessageElement extends HTMLElement { | ||||||
|   // static observedAttributes = ['data-uid', 'data-color', 'data-channel_uid', 'data-user_nick', 'data-created_at', 'data-user_uid'];
 |   // static observedAttributes = ['data-uid', 'data-color', 'data-channel_uid', 'data-user_nick', 'data-created_at', 'data-user_uid'];
 | ||||||
| 
 | 
 | ||||||
|   isVisible() { |  | ||||||
|     if (!this) return false; |  | ||||||
|     const rect = this.getBoundingClientRect(); |  | ||||||
|     return ( |  | ||||||
|       rect.top >= 0 && |  | ||||||
|       rect.left >= 0 && |  | ||||||
|       rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && |  | ||||||
|       rect.right <= (window.innerWidth || document.documentElement.clientWidth) |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   updateUI() { |   updateUI() { | ||||||
|     if (this._originalChildren === undefined) { |     if (this._originalChildren === undefined) { | ||||||
|       const { color,  user_nick, created_at, user_uid} = this.dataset; |       const { color,  user_nick, created_at, user_uid} = this.dataset; | ||||||
| @ -104,8 +93,8 @@ class MessageElement extends HTMLElement { | |||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!this.siblingGenerated && this.nextElementSibling) { |     if ((!this.siblingGenerated || this.siblingGenerated !== this.nextElementSibling) && this.nextElementSibling) { | ||||||
|       this.siblingGenerated = true; |       this.siblingGenerated = this.nextElementSibling; | ||||||
|       if (this.nextElementSibling?.dataset?.user_uid !== this.dataset.user_uid) { |       if (this.nextElementSibling?.dataset?.user_uid !== this.dataset.user_uid) { | ||||||
|         this.classList.add('switch-user'); |         this.classList.add('switch-user'); | ||||||
|       } else { |       } else { | ||||||
| @ -177,6 +166,10 @@ class MessageList extends HTMLElement { | |||||||
|       threshold: 0, |       threshold: 0, | ||||||
|     }) |     }) | ||||||
| 
 | 
 | ||||||
|  |     this.endOfMessages = document.createElement('div'); | ||||||
|  |     this.endOfMessages.classList.add('message-list-bottom'); | ||||||
|  |     this.prepend(this.endOfMessages); | ||||||
|  | 
 | ||||||
|     for(const c of this.children) { |     for(const c of this.children) { | ||||||
|       this._observer.observe(c); |       this._observer.observe(c); | ||||||
|       if (c instanceof MessageElement) { |       if (c instanceof MessageElement) { | ||||||
| @ -184,10 +177,6 @@ class MessageList extends HTMLElement { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     this.endOfMessages = document.createElement('div'); |  | ||||||
|     this.endOfMessages.classList.add('message-list-bottom'); |  | ||||||
|     this.prepend(this.endOfMessages); |  | ||||||
| 
 |  | ||||||
|     this.scrollToBottom(true); |     this.scrollToBottom(true); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -243,13 +232,13 @@ class MessageList extends HTMLElement { | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|   isScrolledToBottom() { |   isScrolledToBottom() { | ||||||
|     return this.isElementVisible(this.endOfMessages); |     return this.visibleSet.has(this.endOfMessages) | ||||||
|   } |   } | ||||||
|   scrollToBottom(force = false, behavior= 'instant') { |   scrollToBottom(force = false, behavior= 'instant') { | ||||||
|     if (force || !this.isScrolledToBottom()) { |     if (force || !this.isScrolledToBottom()) { | ||||||
|       this.firstElementChild.scrollIntoView({ behavior, block: 'end' }); |       this.endOfMessages.scrollIntoView({ behavior, block: 'end' }); | ||||||
|       setTimeout(() => { |       setTimeout(() => { | ||||||
|         this.firstElementChild.scrollIntoView({ behavior, block: 'end' }); |         this.endOfMessages.scrollIntoView({ behavior, block: 'end' }); | ||||||
|       }, 200); |       }, 200); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @ -302,7 +291,7 @@ class MessageList extends HTMLElement { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const scrolledToBottom = this.isScrolledToBottom(); |     const scrolledToBottom = this.isScrolledToBottom(); | ||||||
|     this.prepend(message); |     this.endOfMessages.after(message); | ||||||
|     if (scrolledToBottom) this.scrollToBottom(true); |     if (scrolledToBottom) this.scrollToBottom(true); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -142,10 +142,9 @@ export class Socket extends EventHandler { | |||||||
|       method, |       method, | ||||||
|       args, |       args, | ||||||
|     }; |     }; | ||||||
|     const me = this; |  | ||||||
|     return new Promise((resolve) => { |     return new Promise((resolve) => { | ||||||
|       me.addEventListener(call.callId, (data) => resolve(data)); |       this.addEventListener(call.callId, (data) => resolve(data), { once: true}); | ||||||
|       me.sendJson(call); |       this.sendJson(call); | ||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -82,6 +82,32 @@ emoji.EMOJI_DATA[ | |||||||
| ALLOWED_TAGS = list(bleach.sanitizer.ALLOWED_TAGS) + ["picture"] | ALLOWED_TAGS = list(bleach.sanitizer.ALLOWED_TAGS) + ["picture"] | ||||||
| 
 | 
 | ||||||
| def sanitize_html(value): | def sanitize_html(value): | ||||||
|  | 
 | ||||||
|  |     soup = BeautifulSoup(value, 'html.parser') | ||||||
|  | 
 | ||||||
|  |     for script in soup.find_all('script'): | ||||||
|  |         script.decompose() | ||||||
|  | 
 | ||||||
|  |     #for iframe in soup.find_all('iframe'): | ||||||
|  |         #iframe.decompose() | ||||||
|  | 
 | ||||||
|  |     for tag in soup.find_all(['object', 'embed']): | ||||||
|  |         tag.decompose() | ||||||
|  | 
 | ||||||
|  |     for tag in soup.find_all(): | ||||||
|  |         event_attributes = ['onclick', 'onerror', 'onload', 'onmouseover', 'onfocus'] | ||||||
|  |         for attr in event_attributes: | ||||||
|  |             if attr in tag.attrs: | ||||||
|  |                 del tag[attr] | ||||||
|  | 
 | ||||||
|  |     for img in soup.find_all('img'): | ||||||
|  |         if 'onerror' in img.attrs: | ||||||
|  |             img.decompose() | ||||||
|  | 
 | ||||||
|  |     return soup.prettify() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def sanitize_html2(value): | ||||||
|     return bleach.clean( |     return bleach.clean( | ||||||
|         value, |         value, | ||||||
|         protocols=list(bleach.sanitizer.ALLOWED_PROTOCOLS) + ["data"], |         protocols=list(bleach.sanitizer.ALLOWED_PROTOCOLS) + ["data"], | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ function showTerm(options){ | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class StarField { | class StarField { | ||||||
|   constructor({ count = 100, container = document.body } = {}) { |   constructor({ count = 50, container = document.body } = {}) { | ||||||
|     this.container = container; |     this.container = container; | ||||||
|     this.starCount = count; |     this.starCount = count; | ||||||
|     this.stars = []; |     this.stars = []; | ||||||
| @ -567,7 +567,7 @@ const count = Array.from(messages).filter(el => el.textContent.trim() === text). | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const starField = new StarField({starCount: 100}); | const starField = new StarField({starCount: 50}); | ||||||
| app.starField = starField; | app.starField = starField; | ||||||
| 
 | 
 | ||||||
| class DemoSequence { | class DemoSequence { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user