From 3dcb8b5b1e62b0d0669621a9405551ebf3dcc3e5 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Sat, 2 Mar 2019 21:59:50 +0000
Subject: [PATCH] Update parser engine to handle comments as untrusted

---
 Changelog.md                 | 15 +++++++++++++++
 core/70-parser-engine.php    |  7 ++++---
 modules/feature-comments.php |  2 +-
 modules/parser-parsedown.php |  7 ++++---
 peppermint.guiconfig.json    |  3 ++-
 5 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 9bdc0f8..28c7cbc 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,6 +1,21 @@
 # Changelog
 This file holds the changelog for Pepperminty Wiki. This is the master list of things that have changed (second only to the commit history!) - though the information for any particular release can also be found in the description of it's page for every release made on GitHub too.
 
+
+## v0.19-dev
+> The update that changed the world! Turn everything upside-down.
+
+### Added
+ - [Module API] Added new extra data system. See `parser-parsedown` and `page-edit` for an example.
+     - Extra data is packed into a zip archive, which is packed into `index.php` via [`__halt_compiler();`](https://devdocs.io/php/function.halt-compiler)
+
+### Changed
+ - Core sharding: split `core.php` into multiple files
+
+### Removed
+ - [Module API] Not often I have a removed section! Remove remote file system.
+
+
 ## v0.18
 
 _(No changes have been made since the last beta release.)_
diff --git a/core/70-parser-engine.php b/core/70-parser-engine.php
index 771b25a..c8f6528 100644
--- a/core/70-parser-engine.php
+++ b/core/70-parser-engine.php
@@ -34,10 +34,11 @@ function add_parser($name, $parser_code, $hash_generator) {
  * The specified parser may (though it's unlikely) render it to other things.
  * @package core
  * @param	string	$source		The source to render.
- * @param	string	$use_cache	Whether to use the on-disk cache. Has no effect if parser caching is disabled in peppermint.json, or the source string is too small.
+ * @param	bool	$use_cache	Whether to use the on-disk cache. Has no effect if parser caching is disabled in peppermint.json, or the source string is too small.
+ * @param	bool	$untrusted	Whether the source string is 'untrusted' - i.e. a user comment. Untrusted source disallows HTML and protects against XSS attacks.
  * @return	string	The source rendered to HTML.
  */
-function parse_page_source($source, $use_cache = true) {
+function parse_page_source($source, $untrusted = false, $use_cache = true) {
 	global $settings, $paths, $parsers, $version;
 	$start_time = microtime(true);
 	
@@ -60,7 +61,7 @@ function parse_page_source($source, $use_cache = true) {
 		$result .= "\n<!-- cache: hit, id: $cache_id, took: " . round((microtime(true) - $start_time)*1000, 5) . "ms -->\n";
 	}
 	if($result == null) {
-		$result = $parsers[$settings->parser]["parser"]($source);
+		$result = $parsers[$settings->parser]["parser"]($source, $untrusted);
 		// If we should use the cache and we failed to write to it, warn the admin.
 		// It's not terribible if we can't write to the cache directory (so we shouldn't stop dead & refuse service), but it's still of concern.
 		if($use_cache && !file_put_contents($cache_file, $result))
diff --git a/modules/feature-comments.php b/modules/feature-comments.php
index e9dc398..642de23 100644
--- a/modules/feature-comments.php
+++ b/modules/feature-comments.php
@@ -461,7 +461,7 @@ function render_comments($comments_data, $depth = 0)
 		$result .= "\t<div class='comment' id='comment-$comment->id' data-comment-id='$comment->id'>\n";
 		$result .= "\t<p class='comment-header'><span class='name'>" . page_renderer::render_username($comment->username ?? "<em>Unknown</em>") . "</span> said:</p>";
 		$result .= "\t<div class='comment-body'>\n";
-		$result .= "\t\t" . parse_page_source($comment->message);
+		$result .= "\t\t" . parse_page_source($comment->message, true);
 		$result .= "\t</div>\n";
 		$result .= "\t<div class='reply-box-container'></div>\n";
 		$result .= "\t<p class='comment-footer'>";
diff --git a/modules/parser-parsedown.php b/modules/parser-parsedown.php
index 56da21c..c60a6d6 100644
--- a/modules/parser-parsedown.php
+++ b/modules/parser-parsedown.php
@@ -18,8 +18,9 @@ register_module([
 		
 		$parser = new PeppermintParsedown();
 		$parser->setInternalLinkBase("?page=%s");
-		add_parser("parsedown", function($source) use ($parser) {
+		add_parser("parsedown", function($source, $untrusted) use ($parser) {
 			global $settings;
+			$parser->setsafeMode($untrusted || $settings->all_untrusted);
 			$parser->setMarkupEscaped($settings->clean_raw_html);
 			$result = $parser->text($source);
 			
@@ -429,8 +430,8 @@ class PeppermintParsedown extends ParsedownExtra
 	
 	/**
 	 * Handles parsing out templates - recursively - and the parameter stack associated with it.
-	 * @param  [type] $source [description]
-	 * @return [type]         [description]
+	 * @param	string	$source		The source string to process.
+	 * @return	array	The parsed result
 	 */
 	protected function templateHandler($source)
 	{
diff --git a/peppermint.guiconfig.json b/peppermint.guiconfig.json
index 0a7c602..20dbb45 100644
--- a/peppermint.guiconfig.json
+++ b/peppermint.guiconfig.json
@@ -21,7 +21,8 @@
 	"parser_cache": { "type": "checkbox", "description": "Whether parser output should be cached to speed things up. The cache directory is <code>._cache</code> in the data directory - delete it if you experience issues (unlikely).", "default": true },
 	"parser_cache_min_size": { "type": "number", "description": "The minimum size a source string must be (in bytes) before it's considered eligible for caching.", "default": 1024 },
 	"interwiki_index_location": { "type": "text", "description": "The location to find the interwiki wiki definition file, which contains a list of wikis along with their names, prefixes, and root urls. May be a URL, or simply a file path - as it's passed to file_get_contents(). If left blank, interwiki link parsing is disabled.", "default": null },
-	"clean_raw_html": { "type": "checkbox", "description": "Whether page sources should be cleaned of HTML before rendering. It is STRONGLY recommended that you keep this option turned on.", "default": true},
+	"clean_raw_html": { "type": "checkbox", "description": "Whether page sources should be cleaned of HTML before rendering. It is STRONGLY recommended that you keep this option turned on.", "default": true },
+	"all_untrusted": { "type": "checkbox", "description": "Whether to treat both page sources and comment text as untrusted input. Untrusted input has additional restrictions to protect against XSS attacks etc. Turn on if your wiki allows anonymous edits.", "default": false},
 	"enable_math_rendering": { "type": "checkbox", "description": "Whether to enable client side rendering of mathematical expressions with MathJax (https://www.mathjax.org/). Math expressions should be enclosed inside of dollar signs ($). Turn off if you don't use it.", "default": true},
 	"users": { "type": "usertable", "description": "An array of usernames and passwords - passwords should be hashed with password_hash() (the hash action can help here)", "default": {
 		"admin": {
