Skip to content

Commit 0332a8b

Browse files
xusheng6claude
andauthored
Respect the spoiler flag on comments shown from user profiles (#178)
Comments marked as spoilers were rendered hidden on the crackme page but shown in full on the author's profile Comments tab, leaking the content the spoiler mark is meant to conceal (#148). Wrap spoiler comments on the profile page with the same click-to-reveal behavior used on the crackme page. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d8ae600 commit 0332a8b

2 files changed

Lines changed: 74 additions & 1 deletion

File tree

templates/user/read.html

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,29 @@
99
document.getElementById(id2).style.display = 'none';
1010
document.getElementById(id1).style.display = 'block';
1111
}
12+
13+
function revealSpoiler(id) {
14+
var hidden = document.getElementById('spoiler-hidden-' + id);
15+
var content = document.getElementById('spoiler-content-' + id);
16+
if (hidden && content) {
17+
hidden.style.display = 'none';
18+
content.style.display = 'inline';
19+
}
20+
}
1221
</script>
22+
<style>
23+
.spoiler-hidden {
24+
background-color: #333;
25+
color: #333;
26+
padding: 2px 8px;
27+
border-radius: 3px;
28+
cursor: pointer;
29+
user-select: none;
30+
}
31+
.spoiler-hidden:hover {
32+
background-color: #555;
33+
}
34+
</style>
1335
<div class="container grid-lg wrapper">
1436
<h3><a href="">{{ username }}</a>'s profile</h3>
1537
<div class="columns col-12 ">
@@ -131,7 +153,13 @@ <h3>Comments</h3>
131153
{% for comment in comments %}
132154
<tr class="text-center">
133155
<td><a href="/crackme/{{ comment.crackmehexid }}">{{ comment.crackmename }}</a></td>
134-
<td> <span style="white-space: pre-line">{{ comment.info }}</span> </td>
156+
<td>
157+
{% if comment.spoiler %}
158+
<span id="spoiler-hidden-{{ comment._id }}" class="spoiler-hidden" onclick="revealSpoiler('{{ comment._id }}')">[Click to reveal]</span><span id="spoiler-content-{{ comment._id }}" style="display: none; white-space: pre-line">{{ comment.info }}</span>
159+
{% else %}
160+
<span style="white-space: pre-line">{{ comment.info }}</span>
161+
{% endif %}
162+
</td>
135163
<td>{{ comment.created_at|PRETTYTIME }}</td>
136164
</tr>
137165
{% endfor %}

tests/test_routes.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,48 @@ def test_existing_user_profile_loads(client, alice):
9090

9191
def test_missing_user_profile_is_404(client):
9292
assert client.get('/user/missing').status_code == 404
93+
94+
95+
def test_spoiler_comment_is_hidden_on_user_profile(client, db, alice):
96+
from bson import ObjectId
97+
from datetime import datetime, timezone
98+
99+
secret = 'the flag is 1234'
100+
db.comment.insert_one({
101+
'_id': ObjectId(),
102+
'info': secret,
103+
'author': 'alice',
104+
'crackmehexid': 'deadbeef',
105+
'crackmename': 'Test Crackme',
106+
'created_at': datetime.now(timezone.utc),
107+
'visible': True,
108+
'deleted': False,
109+
'spoiler': True,
110+
})
111+
112+
html = client.get('/user/alice').get_data(as_text=True)
113+
# The spoiler content is present but wrapped so it is hidden by default.
114+
assert '[Click to reveal]' in html
115+
assert 'spoiler-content-' in html
116+
assert 'revealSpoiler(' in html
117+
118+
119+
def test_non_spoiler_comment_is_shown_on_user_profile(client, db, alice):
120+
from bson import ObjectId
121+
from datetime import datetime, timezone
122+
123+
db.comment.insert_one({
124+
'_id': ObjectId(),
125+
'info': 'just a normal comment',
126+
'author': 'alice',
127+
'crackmehexid': 'deadbeef',
128+
'crackmename': 'Test Crackme',
129+
'created_at': datetime.now(timezone.utc),
130+
'visible': True,
131+
'deleted': False,
132+
'spoiler': False,
133+
})
134+
135+
html = client.get('/user/alice').get_data(as_text=True)
136+
assert 'just a normal comment' in html
137+
assert '[Click to reveal]' not in html

0 commit comments

Comments
 (0)