diff --git a/CHANGELOG.md b/CHANGELOG.md index e0b970b..a9cff54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## CHANGELOG +### [25.2.2] - Jan 7, 2025 +- Don't send sensitive metadata in fetched DataTemplates as it isn't needed +- Fixed an issue where we broke CSP due to our use of `javascript:void(0)` to stop hashes appearing within the URL + ### [25.2.1] - Jan 6, 2025 - Fixed a regression when adding support for "Delete Link" @@ -377,6 +381,7 @@ - Initial release +[25.2.2]: https://github.com/cmason3/jinjafx_server/compare/25.2.1...25.2.2 [25.2.1]: https://github.com/cmason3/jinjafx_server/compare/25.2.0...25.2.1 [25.2.0]: https://github.com/cmason3/jinjafx_server/compare/25.1.1...25.2.0 [25.1.1]: https://github.com/cmason3/jinjafx_server/compare/25.1.0...25.1.1 diff --git a/jinjafx_server.py b/jinjafx_server.py index 0b605dc..c98b30a 100755 --- a/jinjafx_server.py +++ b/jinjafx_server.py @@ -26,7 +26,7 @@ import re, argparse, hashlib, traceback, glob, hmac, uuid, struct, binascii, gzip, requests, ctypes, subprocess import cmarkgfm, emoji -__version__ = '25.2.1' +__version__ = '25.2.2' llock = threading.RLock() rlock = threading.RLock() @@ -258,13 +258,18 @@ def do_GET(self, head=False, cache=True, versioned=False): dt = '' self.critical = True + def sanitise_dt(dt): + fields = ('dt_password:', 'dt_mpassword:', 'remote_addr:') + dt = '\n'.join([ln for ln in dt.splitlines() if not ln.startswith(fields)]) + return dt.encode('utf-8') + if aws_s3_url or github_url or repository: if not self.ratelimit(remote_addr, 2, False): if aws_s3_url: rr = aws_s3_get(aws_s3_url, 'jfx_' + fpath[8:] + '.yml') if rr.status_code == 200: - r = [ 'application/json', 200, json.dumps({ 'dt': self.e(rr.text.encode('utf-8')).decode('utf-8') }).encode('utf-8'), sys._getframe().f_lineno ] + r = [ 'application/json', 200, json.dumps({ 'dt': self.e(sanitise_dt(rr.text)).decode('utf-8') }).encode('utf-8'), sys._getframe().f_lineno ] dt = rr.text @@ -284,7 +289,7 @@ def do_GET(self, head=False, cache=True, versioned=False): if jobj.get('encoding') and jobj.get('encoding') == 'base64': content = base64.b64decode(content).decode('utf-8') - r = [ 'application/json', 200, json.dumps({ 'dt': self.e(content.encode('utf-8')).decode('utf-8') }).encode('utf-8'), sys._getframe().f_lineno ] + r = [ 'application/json', 200, json.dumps({ 'dt': self.e(sanitise_dt(content)).decode('utf-8') }).encode('utf-8'), sys._getframe().f_lineno ] dt = content @@ -299,10 +304,9 @@ def do_GET(self, head=False, cache=True, versioned=False): if os.path.isfile(fpath): with open(fpath, 'rb') as f: - rr = f.read() - dt = rr.decode('utf-8') + dt = f.read().decode('utf-8') - r = [ 'application/json', 200, json.dumps({ 'dt': self.e(rr).decode('utf-8') }).encode('utf-8'), sys._getframe().f_lineno ] + r = [ 'application/json', 200, json.dumps({ 'dt': self.e(sanitise_dt(dt)).decode('utf-8') }).encode('utf-8'), sys._getframe().f_lineno ] else: r = [ 'text/plain', 404, '404 Not Found\r\n'.encode('utf-8'), sys._getframe().f_lineno ] @@ -312,7 +316,7 @@ def do_GET(self, head=False, cache=True, versioned=False): if 'X-Dt-Password' in self.headers: try: dt = jinjafx.Vaulty().decrypt(dt, self.headers['X-Dt-Password']) - r = [ 'application/json', 200, json.dumps({ 'dt': self.e(dt.encode('utf-8')).decode('utf-8') }).encode('utf-8'), sys._getframe().f_lineno ] + r = [ 'application/json', 200, json.dumps({ 'dt': self.e(sanitise_dt(dt)).decode('utf-8') }).encode('utf-8'), sys._getframe().f_lineno ] except Exception: cheaders['X-Dt-Authentication'] = 'Open' @@ -752,6 +756,9 @@ def authenticate_dt(rdt, r): if dt_encrypted: dt_yml += 'encrypted: 1\n' + if dt_protected: + dt_yml += 'protected: 1\n' + def update_dt(rdt, dt_yml, r): mm, mo, r = authenticate_dt(rdt, r) diff --git a/www/index.html b/www/index.html index e26c250..7f65f49 100644 --- a/www/index.html +++ b/www/index.html @@ -32,7 +32,7 @@ - +
@@ -100,25 +100,25 @@
diff --git a/www/jinjafx_m.js b/www/jinjafx_m.js index 5d0112e..bc2bab0 100644 --- a/www/jinjafx_m.js +++ b/www/jinjafx_m.js @@ -197,13 +197,12 @@ function getStatusText(code) { document.getElementById('datasets').innerHTML = ''; Object.keys(datasets).sort(default_on_top).forEach(function(ds) { - var a = document.createElement('a'); - a.classList.add('dropdown-item', 'text-decoration-none'); - a.addEventListener('click', select_dataset, false); - a.href = 'javascript:void(0)'; - a.ds_name = ds; - a.innerHTML = ds; - document.getElementById('datasets').appendChild(a); + var e = document.createElement('button'); + e.classList.add('dropdown-item', 'text-decoration-none'); + e.addEventListener('click', select_dataset, false); + e.ds_name = ds; + e.innerHTML = ds; + document.getElementById('datasets').appendChild(e); }); if (Object.keys(datasets).length > 1) { @@ -250,13 +249,12 @@ function getStatusText(code) { document.getElementById('templates').innerHTML = ''; Object.keys(templates).sort(default_on_top).forEach(function(t) { - var a = document.createElement('a'); - a.classList.add('dropdown-item', 'text-decoration-none'); - a.addEventListener('click', select_template, false); - a.href = 'javascript:void(0)'; - a.t_name = t; - a.innerHTML = t; - document.getElementById('templates').appendChild(a); + var e = document.createElement('button'); + e.classList.add('dropdown-item', 'text-decoration-none'); + e.addEventListener('click', select_template, false); + e.t_name = t; + e.innerHTML = t; + document.getElementById('templates').appendChild(e); }); if (Object.keys(templates).length > 1) { @@ -909,7 +907,7 @@ function getStatusText(code) { document.getElementById('get').classList.add('d-none'); document.getElementById('mdd').disabled = false; - if (dt.hasOwnProperty('dt_password') || dt.hasOwnProperty('dt_mpassword')) { + if (dt.hasOwnProperty('protected') && (dt['protected'] === 1)) { document.getElementById('protect_text').innerHTML = 'Update Protection'; dt_protected = true; }