-
Notifications
You must be signed in to change notification settings - Fork 0
Fix PR review bugs, add SetUserSettings/GetUserSettings, remove rate limiting #14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,52 @@ | ||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||
| * Copyright (C) 2023-2025 XMOJ-bbs contributors | ||||||||||||||||||||||||||||||||||||
| * This file is part of XMOJ-bbs. | ||||||||||||||||||||||||||||||||||||
| * XMOJ-bbs is free software: you can redistribute it and/or modify | ||||||||||||||||||||||||||||||||||||
| * it under the terms of the GNU Affero General Public License as published by | ||||||||||||||||||||||||||||||||||||
| * the Free Software Foundation, either version 3 of the License, or | ||||||||||||||||||||||||||||||||||||
| * (at your option) any later version. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * XMOJ-bbs is distributed in the hope that it will be useful, | ||||||||||||||||||||||||||||||||||||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||||||||||||||||||||||||||||||||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||||||||||||||||||||||||||||||||||
| * GNU Affero General Public License for more details. | ||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||
| * You should have received a copy of the GNU Affero General Public License | ||||||||||||||||||||||||||||||||||||
| * along with XMOJ-bbs. If not, see <https://www.gnu.org/licenses/>. | ||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| import { Result, ThrowErrorIfFailed } from "~/utils/resultUtils"; | ||||||||||||||||||||||||||||||||||||
| import { Output } from "~/utils/output"; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| export default eventHandler(async (event) => { | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| const { auth } = event.context; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const SettingsData: any[] = ThrowErrorIfFailed( | ||||||||||||||||||||||||||||||||||||
| await auth.database.Select("user_settings", ["settings"], { | ||||||||||||||||||||||||||||||||||||
| user_id: auth.username | ||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (SettingsData.length === 0) { | ||||||||||||||||||||||||||||||||||||
| return new Result(true, "获得设置成功", { Settings: {} }); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let SettingsObject: object; | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| SettingsObject = JSON.parse(SettingsData[0]["settings"]); | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+35
to
+37
|
||||||||||||||||||||||||||||||||||||
| let SettingsObject: object; | |
| try { | |
| SettingsObject = JSON.parse(SettingsData[0]["settings"]); | |
| // Handle potential multiple rows deterministically by selecting a specific row. | |
| let selectedSettingsRow = SettingsData[0]; | |
| if (SettingsData.length > 1) { | |
| const sortedSettingsData = [...SettingsData].sort((a, b) => { | |
| const sa = typeof a.settings === "string" ? a.settings : JSON.stringify(a.settings); | |
| const sb = typeof b.settings === "string" ? b.settings : JSON.stringify(b.settings); | |
| return sa.localeCompare(sb); | |
| }); | |
| selectedSettingsRow = sortedSettingsData[sortedSettingsData.length - 1]; | |
| } | |
| let SettingsObject: object; | |
| try { | |
| SettingsObject = JSON.parse(selectedSettingsRow["settings"]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Align the parsed settings type with a more precise structure instead of object.
Here you rely on SettingsObject being a non-null, non-array object, but it’s typed as object. Typing this as unknown (with runtime narrowing) or Record<string, unknown> would better reflect the checks you perform and keep the types aligned with the actual shape of the data.
Suggested implementation:
let SettingsObject: unknown;
try {
SettingsObject = JSON.parse(SettingsData[0]["settings"]);
} catch (_) {
return new Result(false, "设置数据损坏");
}
if (typeof SettingsObject !== "object" || Array.isArray(SettingsObject) || SettingsObject === null) {
return new Result(false, "设置数据损坏");
}
const TypedSettingsObject = SettingsObject as Record<string, unknown>;Anywhere later in this function where SettingsObject was used as an object, switch to TypedSettingsObject so that you benefit from the more precise Record<string, unknown> type. If you prefer to keep the original variable name, you can instead reassign with a type assertion, e.g. SettingsObject = SettingsObject as Record<string, unknown>; and adjust the code accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚨 issue (security): Return a generic error message instead of propagating internal error text.
The raw errorMsg is still included in the client-facing Result. To avoid leaking internal error details, keep the full message only in Output.Error and return a stable, user-friendly error string or code instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Do not return raw internal exception messages to clients; return a generic failure message and keep details only in logs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/routes/GetUserSettings.ts, line 50:
<comment>Do not return raw internal exception messages to clients; return a generic failure message and keep details only in logs.</comment>
<file context>
@@ -0,0 +1,52 @@
+ if (error instanceof Result) return error;
+ const errorMsg = error instanceof Error ? error.message : String(error);
+ Output.Error("GetUserSettings error: " + errorMsg);
+ return new Result(false, "获得设置失败: " + errorMsg);
+ }
+});
</file context>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This handler assumes
event.context.authis always set; if auth middleware returns early without populating it,auth.database.Select(...)will throw and the route will respond with an internal error message. Add an explicitif (!auth || !auth.database) return new Result(false, "身份验证失败");guard before DB access.