Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
canifa_note
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
canifa_note
Commits
6b0f90f3
Commit
6b0f90f3
authored
Dec 20, 2025
by
Johnny
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: prevent sensitive data caching
parent
eaef04a0
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
35 additions
and
5 deletions
+35
-5
user_service.go
server/router/api/v1/user_service.go
+18
-0
frontend.go
server/router/frontend/frontend.go
+5
-1
index.html
web/index.html
+3
-0
UserMenu.tsx
web/src/components/UserMenu.tsx
+3
-1
connect.ts
web/src/connect.ts
+3
-1
RootLayout.tsx
web/src/layouts/RootLayout.tsx
+3
-2
No files found.
server/router/api/v1/user_service.go
View file @
6b0f90f3
...
@@ -238,6 +238,24 @@ func (s *APIV1Service) UpdateUser(ctx context.Context, request *v1pb.UpdateUserR
...
@@ -238,6 +238,24 @@ func (s *APIV1Service) UpdateUser(ctx context.Context, request *v1pb.UpdateUserR
case
"email"
:
case
"email"
:
update
.
Email
=
&
request
.
User
.
Email
update
.
Email
=
&
request
.
User
.
Email
case
"avatar_url"
:
case
"avatar_url"
:
// Validate avatar MIME type to prevent XSS during upload
if
request
.
User
.
AvatarUrl
!=
""
{
imageType
,
_
,
err
:=
extractImageInfo
(
request
.
User
.
AvatarUrl
)
if
err
!=
nil
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"invalid avatar format: %v"
,
err
)
}
// Only allow safe image formats for avatars
allowedAvatarTypes
:=
map
[
string
]
bool
{
"image/png"
:
true
,
"image/jpeg"
:
true
,
"image/jpg"
:
true
,
"image/gif"
:
true
,
"image/webp"
:
true
,
}
if
!
allowedAvatarTypes
[
imageType
]
{
return
nil
,
status
.
Errorf
(
codes
.
InvalidArgument
,
"invalid avatar image type: %s. Only PNG, JPEG, GIF, and WebP are allowed"
,
imageType
)
}
}
update
.
AvatarURL
=
&
request
.
User
.
AvatarUrl
update
.
AvatarURL
=
&
request
.
User
.
AvatarUrl
case
"description"
:
case
"description"
:
update
.
Description
=
&
request
.
User
.
Description
update
.
Description
=
&
request
.
User
.
Description
...
...
server/router/frontend/frontend.go
View file @
6b0f90f3
...
@@ -35,8 +35,12 @@ func (*FrontendService) Serve(_ context.Context, e *echo.Echo) {
...
@@ -35,8 +35,12 @@ func (*FrontendService) Serve(_ context.Context, e *echo.Echo) {
if
util
.
HasPrefixes
(
c
.
Path
(),
"/api"
,
"/memos.api.v1"
)
{
if
util
.
HasPrefixes
(
c
.
Path
(),
"/api"
,
"/memos.api.v1"
)
{
return
true
return
true
}
}
// Skip setting cache headers for index.html
// For index.html and root path, set no-cache headers to prevent browser caching
// This prevents sensitive data from being accessible via browser back button after logout
if
c
.
Path
()
==
"/"
||
c
.
Path
()
==
"/index.html"
{
if
c
.
Path
()
==
"/"
||
c
.
Path
()
==
"/index.html"
{
c
.
Response
()
.
Header
()
.
Set
(
echo
.
HeaderCacheControl
,
"no-cache, no-store, must-revalidate"
)
c
.
Response
()
.
Header
()
.
Set
(
"Pragma"
,
"no-cache"
)
c
.
Response
()
.
Header
()
.
Set
(
"Expires"
,
"0"
)
return
false
return
false
}
}
// Set Cache-Control header for static assets.
// Set Cache-Control header for static assets.
...
...
web/index.html
View file @
6b0f90f3
...
@@ -2,6 +2,9 @@
...
@@ -2,6 +2,9 @@
<html
lang=
"en"
>
<html
lang=
"en"
>
<head>
<head>
<meta
charset=
"UTF-8"
/>
<meta
charset=
"UTF-8"
/>
<meta
http-equiv=
"Cache-Control"
content=
"no-cache, no-store, must-revalidate"
/>
<meta
http-equiv=
"Pragma"
content=
"no-cache"
/>
<meta
http-equiv=
"Expires"
content=
"0"
/>
<link
rel=
"apple-touch-icon"
sizes=
"180x180"
href=
"/apple-touch-icon.png"
/>
<link
rel=
"apple-touch-icon"
sizes=
"180x180"
href=
"/apple-touch-icon.png"
/>
<link
rel=
"icon"
type=
"image/webp"
href=
"/logo.webp"
/>
<link
rel=
"icon"
type=
"image/webp"
href=
"/logo.webp"
/>
<link
rel=
"manifest"
href=
"/site.webmanifest"
/>
<link
rel=
"manifest"
href=
"/site.webmanifest"
/>
...
...
web/src/components/UserMenu.tsx
View file @
6b0f90f3
...
@@ -64,7 +64,9 @@ const UserMenu = observer((props: Props) => {
...
@@ -64,7 +64,9 @@ const UserMenu = observer((props: Props) => {
keysToRemove
.
forEach
((
key
)
=>
localStorage
.
removeItem
(
key
));
keysToRemove
.
forEach
((
key
)
=>
localStorage
.
removeItem
(
key
));
window
.
location
.
href
=
Routes
.
AUTH
;
// Use replace() instead of href to prevent back button from showing cached sensitive data
// This removes the current page from browser history
window
.
location
.
replace
(
Routes
.
AUTH
);
};
};
return
(
return
(
...
...
web/src/connect.ts
View file @
6b0f90f3
...
@@ -92,7 +92,9 @@ function getAuthFailureRedirect(currentPath: string): string | null {
...
@@ -92,7 +92,9 @@ function getAuthFailureRedirect(currentPath: string): string | null {
function
performRedirect
(
redirectUrl
:
string
|
null
):
void
{
function
performRedirect
(
redirectUrl
:
string
|
null
):
void
{
if
(
redirectUrl
)
{
if
(
redirectUrl
)
{
window
.
location
.
href
=
redirectUrl
;
// Use replace() instead of href to prevent back button from showing cached sensitive data
// This removes the current page from browser history after authentication failure
window
.
location
.
replace
(
redirectUrl
);
}
}
}
}
...
...
web/src/layouts/RootLayout.tsx
View file @
6b0f90f3
...
@@ -24,12 +24,13 @@ const RootLayout = observer(() => {
...
@@ -24,12 +24,13 @@ const RootLayout = observer(() => {
if
(
!
currentUser
)
{
if
(
!
currentUser
)
{
// If disallowPublicVisibility is enabled, redirect to the login page if the user is not logged in.
// If disallowPublicVisibility is enabled, redirect to the login page if the user is not logged in.
if
(
instanceStore
.
state
.
memoRelatedSetting
.
disallowPublicVisibility
)
{
if
(
instanceStore
.
state
.
memoRelatedSetting
.
disallowPublicVisibility
)
{
window
.
location
.
href
=
Routes
.
AUTH
;
// Use replace() to prevent back button from showing cached sensitive data
window
.
location
.
replace
(
Routes
.
AUTH
);
return
;
return
;
}
else
if
(
}
else
if
(
([
Routes
.
ROOT
,
Routes
.
ATTACHMENTS
,
Routes
.
INBOX
,
Routes
.
ARCHIVED
,
Routes
.
SETTING
]
as
string
[]).
includes
(
location
.
pathname
)
([
Routes
.
ROOT
,
Routes
.
ATTACHMENTS
,
Routes
.
INBOX
,
Routes
.
ARCHIVED
,
Routes
.
SETTING
]
as
string
[]).
includes
(
location
.
pathname
)
)
{
)
{
window
.
location
.
href
=
Routes
.
EXPLORE
;
window
.
location
.
replace
(
Routes
.
EXPLORE
)
;
return
;
return
;
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment