Remove 'Never' expiry (1-year max) and fix Raw blob URL issue

This commit is contained in:
ComputerTech 2026-03-28 01:00:52 +00:00
parent d0289f85d5
commit a46cba7143
4 changed files with 58 additions and 19 deletions

20
app.py
View File

@ -261,10 +261,11 @@ def create_paste():
else:
return jsonify({'error': 'Provide either encrypted_data or content'}), 400
allowed_expiry = set(_pastes.get('allow_expiry_options', ['never']))
expires_in = data.get('expires_in', 'never')
allowed_expiry = set(_pastes.get('allow_expiry_options', ['1year']))
expires_in = data.get('expires_in', _pastes.get('default_expiry', '1year'))
if expires_in not in allowed_expiry:
expires_in = 'never'
# Fallback to the first allowed option if everything is missing
expires_in = _pastes.get('default_expiry', list(allowed_expiry)[0])
expires_at = None
if expires_in != 'never':
@ -273,6 +274,7 @@ def create_paste():
'1day': datetime.timedelta(days=1),
'1week': datetime.timedelta(weeks=1),
'1month': datetime.timedelta(days=30),
'1year': datetime.timedelta(days=365),
}
delta = delta_map.get(expires_in)
if delta is None:
@ -322,15 +324,21 @@ def view_paste_raw(paste_id):
paste = _get_paste_or_abort(paste_id)
stored = paste['encrypted_data']
# Plaintext pastes are stored as a JSON object; return the content directly.
# 1. Plaintext paste — Return content directly as text/plain
if not re.match(r'^[A-Za-z0-9_-]+:[A-Za-z0-9_-]+$', stored):
try:
data = json.loads(stored)
return Response(data.get('content', ''), mimetype='text/plain')
return Response(data.get('content', ''), mimetype='text/plain; charset=utf-8')
except (json.JSONDecodeError, TypeError):
pass
# Encrypted paste — return the raw ciphertext blob for API consumers.
# 2. Encrypted paste — Browsers get a minimal decryptor; API consumers get JSON
accept = request.headers.get('Accept', '')
if 'text/html' in accept:
# Minimal HTML shell that handles decryption for browsers (E2E)
return render_template('raw_decryptor.html', paste=paste)
# API response
return jsonify({
'id': paste['id'],
'encrypted_data': stored,

View File

@ -25,14 +25,14 @@
"id_length": 8,
"recent_limit": 50,
"default_language": "text",
"default_expiry": "never",
"allow_expiry_options": ["never", "1hour", "1day", "1week", "1month"],
"default_expiry": "1year",
"allow_expiry_options": ["1hour", "1day", "1week", "1month", "1year"],
"expiry_labels": {
"never": "Never",
"1hour": "1 Hour",
"1day": "1 Day",
"1week": "1 Week",
"1month": "1 Month"
"1month": "1 Month",
"1year": "1 Year"
}
},

View File

@ -137,15 +137,9 @@ function showError(title, detail) {
}
function rawView() {
if (!_decryptedPaste) return;
const blob = new Blob([_decryptedPaste.content], { type: 'text/plain; charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = Object.assign(document.createElement('a'),
{ href: url, target: '_blank', rel: 'noopener noreferrer' });
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(() => URL.revokeObjectURL(url), 10000);
const key = window.location.hash;
const url = window.location.href.split('?')[0].split('#')[0] + '/raw' + key;
window.open(url, '_blank');
}
async function copyPaste() {

View File

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Raw | {{ cfg.site.name }}</title>
<style>
body { margin: 0; padding: 1rem; background: #fff; color: #000; font-family: monospace; }
pre { white-space: pre-wrap; word-wrap: break-word; margin: 0; }
#error { display: none; color: red; font-weight: bold; }
</style>
</head>
<body>
<pre id="rawContent"></pre>
<div id="error">Decryption Failed — Key missing or incorrect.</div>
<script src="{{ url_for('static', filename='js/crypto.js') }}" nonce="{{ csp_nonce }}"></script>
<script nonce="{{ csp_nonce }}">
(async function decryptRaw() {
const raw = {{ paste.encrypted_data | tojson }};
const keyBase64 = window.location.hash.slice(1);
const pre = document.getElementById('rawContent');
const error = document.getElementById('error');
if (!keyBase64) { error.style.display = 'block'; return; }
try {
const key = await PasteCrypto.importKey(keyBase64);
const plaintext = await PasteCrypto.decrypt(raw, key);
const data = JSON.parse(plaintext);
pre.textContent = data.content;
} catch (e) {
error.style.display = 'block';
}
})();
</script>
</body>
</html>