修改数据看板文字 - #51
Merged
Merged
Conversation
Member
|
谢谢😘 |
sususu98
pushed a commit
to sususu98/new-api
that referenced
this pull request
Dec 1, 2025
* fix: 修复 Session 数据越权访问漏洞, close QuantumNous#51 ## 问题描述 普通用户(非管理员令牌)登录 Web UI 后,可以通过"仪表盘" → "活跃 Session" 查看到其他用户 5 分钟内的请求内容和响应内容,造成严重的数据泄露。 ## 安全影响 - **数据泄露风险**: 用户可以查看其他用户的敏感请求和响应 - **隐私侵犯**: 违反用户数据隔离原则 - **合规风险**: 可能违反数据保护法规(如 GDPR) ## 修复方案 在所有 Session 相关 API 中添加用户权限检查: 1. **getActiveSessions()** - 列表过滤 - 管理员:可查看所有用户的 Session - 普通用户:只能查看自己的 Session 2. **getAllSessions()** - 列表过滤 - 同上,支持活跃和非活跃 Session 3. **getSessionDetails()** - 详情访问控制 - 检查 Session 所有权 - 非所有者尝试访问时记录安全日志并拒绝 4. **getSessionMessages()** - 消息访问控制 - 检查 Session 所有权 - 记录越权访问尝试 5. **getSessionResponse()** - 响应体访问控制 - 检查 Session 所有权 - 记录越权访问尝试 ## 技术实现 - 使用 `getSession()` 获取当前登录用户 - 通过 `aggregateSessionStats()` 查询 Session 的 userId - 比对 `sessionStats.userId` 与 `currentUserId` - 所有越权访问尝试都会记录安全日志 ## 测试建议 1. 创建两个普通用户(user1, user2) 2. user1 发起 API 请求产生 Session 3. user2 登录 Web UI 尝试查看 user1 的 Session 4. 预期结果:user2 只能看到自己的 Session,无法看到 user1 的 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Code Bot <bot@claude.ai> Co-authored-by: Claude <noreply@anthropic.com>
x22x22
pushed a commit
to x22x22/new-api
that referenced
this pull request
Apr 24, 2026
…'s quota correctly (close QuantumNous#51)
x22x22
pushed a commit
to x22x22/new-api
that referenced
this pull request
Apr 24, 2026
x22x22
pushed a commit
to x22x22/new-api
that referenced
this pull request
Apr 24, 2026
haojianggod
pushed a commit
to Hermerz/new-api
that referenced
this pull request
May 23, 2026
Three-prong refinement of QuantumNous#51 Phase 1 UserGroupModelDiscount layer based on BD UX feedback + @wswuai's design review (Hermerz/Hermes#66): 1. Semantic (option A): discount value = customer's final fraction of model official price. When set, bypass GroupRatio entirely so BD's "0.2" input = customer pays 2折 of official directly (no need to reverse-engineer GroupRatio). 2. Log observability: PriceData gains UserGroupDiscount field; GenerateTextOtherInfo writes user_group_discount to log when configured. All wrappers (Text/Wss/Audio/Claude) pick it up via relayInfo.PriceData with no signature changes. 3. UI clarity: UserGroupModelDiscountSettings extraText updated. Lookup key stays info.UserGroup (= user.Group, the customer's home tier) per QuantumNous#51 spec — "客户分层 × 模型" is a user-tier concern, not a token-key concern. QuantumNous#51 explicitly punts "单 key 级别折扣覆盖" out of scope. See decision record in Hermerz/Hermes#66. Also fixes a pre-existing WSS bug surfaced by Codex cross-model review: PreWssConsumeQuota now syncs PriceData.ModelRatio + GroupRatio back to relayInfo so PostWssConsumeQuota settle uses the same values as pre-consume (avoids divergence when autoGroup re-routes mid-session). Follow-ups (non-blocking, separate issues): - Hermerz/Hermes#67: extract applyUserGroupDiscount helper (de-dup the discount apply logic between price.go and quota.go) - Hermerz/Hermes#68: lift discount into PriceData.Discount + apply at settle time for billing observability Reviewed by /god-pr-self-review + Codex Tier 1 cross-model (2 rounds). Refs: Hermerz/Hermes#51, Hermerz/Hermes#66 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
haojianggod
pushed a commit
to Hermerz/new-api
that referenced
this pull request
May 23, 2026
QuantumNous#68 no-bake refactor + QuantumNous#67 LookupUserGroupDiscount helper for the UserGroupModelDiscount layer (QuantumNous#51 Phase 1 option A). Before (commit 472571c): ModelPriceHelper baked discount into modelRatio + forced GroupRatio=1.0 at pre-consume. PriceData.ModelRatio = baked value, GroupRatio = 1.0 for matched requests. Log model_ratio + group_ratio fields lost the "市场基线 vs 客户分层 vs 折扣" decomposition needed for 对账. After (this commit): ModelPriceHelper captures discount RAW into priceData.UserGroupDiscount + leaves ModelRatio + GroupRatio raw. New PriceData.EffectiveGroupRatio() method returns UserGroupDiscount if > 0 else raw GroupRatio. Settle paths (text_quota.go calculateTextQuotaSummary, quota.go PostWss/ PostAudio) use Effective for billing math but pass raw values to log writer. Log gains effective_model_ratio derived field. Math identical: discount=0.2 + GroupRatio=0.37 + ModelRatio=0.625 → final ratio = 0.125 (= 0.625 × 0.2) both before and after. Option A "discount bypasses GroupRatio" semantic preserved. Files: - types/price_data.go: + EffectiveGroupRatio() method, doc updates - relay/helper/price.go: no-bake; uses LookupUserGroupDiscount helper - service/quota.go: PreWss no-bake + sync; PostWss/Audio split effectiveGroupRatio (billing) vs rawGroupRatio (log) - service/text_quota.go: summary.UserGroupDiscount field; dGroupRatio derived as effective (used in ~9 multiplications) - service/log_info_generate.go: + effective_model_ratio log field - setting/ratio_setting/user_group_discount.go: + LookupUserGroupDiscount helper (QuantumNous#67) with sentinel-0 constraint doc Constraint: BD must not configure discount = 0 — silently treated as "not configured" due to sentinel-0 collision. Admin UI validation filed as Hermerz/Hermes#71. Reviewed by /god-pr-self-review + Codex Tier 1 cross-model. Codex flagged 2 Criticals: - C1 (EffectiveGroupRatio drops GroupRatio): rejected as hallucinated diff (Codex referenced BuildPriceData / EffectiveInputRatio functions that don't exist in this codebase; invented modelRatio*groupRatio*discount formula). Verified actual 472571c commit baked modelRatio*discount with GroupRatio=1.0 bypass, identical to current EffectiveGroupRatio semantic. - C2 (sentinel-0 collision): in-scope, addressed via doc warning + follow-up issue. Refs: Hermerz/Hermes#51, QuantumNous#66, QuantumNous#67, QuantumNous#68 Follow-up: Hermerz/Hermes#71 (admin UI validation) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lingozhi
pushed a commit
to lingozhi/new-api
that referenced
this pull request
Jul 17, 2026
Measured over 262 prod requests, all six channels serving the SAME model at the SAME reasoning effort — so the spread is the channel, not the request: #41 1275ms (n=90) QuantumNous#51 1328ms (n=66) QuantumNous#42 2337ms (n=17) #33 3388ms (n=24) QuantumNous#45 3537ms (n=52) QuantumNous#57 8133ms (n=8) Bucketing by uncached prompt tokens holds p50 flat (1612/1934/2073/1802 for 0-2k/2-6k/6-20k/20k+), which rules out prefill volume as the cause. Meanwhile ~100% of requests are affinity-pinned, so the health-weighted selection that was supposed to steer around slow channels almost never runs — the only exit is IsChannelFastEnoughForAffinity, and `score > 0.1` with `score = 1/(1+latency)` only fires past 9s. QuantumNous#45 and QuantumNous#57 kept their sticky traffic forever; 33% of requests were pinned to a channel 2.6x+ slower than the one next to it. Release relative to the best peer for the same model+path instead: leave only when the sticky is BOTH slower than 2s absolute and >2x that peer. The absolute floor stops churn over gaps nobody can feel (0.3s vs 0.15s is 2x), the ratio stops churn for gains too small to repay a migration. QuantumNous#42 at 1.83x stays. The reason this could not just be turned on: migration causes a cold prompt cache by construction, and a cold 240k-token prefill measured 23348ms. That one number breaks two mechanisms at once, both of which then punish the channel we just chose *for being the fastest*: - the latency EWMA, which is shared by every affinity key on the channel, so one migration would make the destination look slow to all of them and stampede them off it, each paying its own cold prefill; - cooldownSlowChannelIfNeeded, an entirely separate path that yanks a channel out of rotation for 30 minutes past 30s FRT — 23.3s is 78% of that. So the release is gated three ways. ColdCacheStart marks the attempt and keeps its latency out of both mechanisms (failures still count — a cold start is no excuse for erroring). A per-key cooldown stops one key bouncing. A global minimum interval drains a slow channel one key at a time, because the slow verdict is per-channel: without it every key pinned to QuantumNous#45 leaves on the same tick and their cold prefills land on #41 together, making it genuinely slow — a cascade no latency-exclusion can prevent, since that load is real. Circuit-open releases stay unconditional and never reach AcquireChannelHealth, or a rate-limited key would be parked on a tripped channel and a paying request would become its half-open probe. Accepted: weighted re-selection can hand a released key back to the same slow channel (~12.5% by weight). Not excluded — that risks a 503, and the cache there is still warm, so the only cost is the 10-minute slot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lingozhi
pushed a commit
to lingozhi/new-api
that referenced
this pull request
Jul 17, 2026
Prod, one bad hour: four Codex sessions, one (1ff18e9d) sat on an idle #17 doing 1-2s the whole time, while another (c34a4d35) churned QuantumNous#51 -> #41 -> QuantumNous#56, every request 8-22s, never once landing on #17. The first-token times the user saw (6-11s) were these bounced sessions. When a session is released from a slow channel (affinity migration, or its sticky channel cooling), it re-picks via weighted-random selection. The health score weighting there under-separates fast from slow: score = 1/(1+EWMA), so 12s vs 1.8s is only ~4.6x. With several slow channels their combined weight rivals the one fast channel, and the bounced session is dropped back onto a slow one about as often as not. That is how c34a4d35 stayed slow. Multiply the selection weight of a channel measured fast right now (EWMA < affinityFastLatency) by 8. A single fast channel then takes ~80-90% of re-picks against a handful of slow peers instead of ~59%, so a bounced session lands on it. It self-limits: the boost keys off the live EWMA, so if the fast channel slows under the added load past the threshold it stops being boosted and traffic redistributes; slow channels keep a small share, which probes them for recovery. When nothing is under the threshold (whole fleet slow) no channel is boosted and selection falls back to plain health weighting — the same "only move toward somewhere genuinely fast" rule the affinity migration logic uses. Both selection paths (memory-cache and DB) now share EffectiveSelectionWeight so they weight identically; previously only one would have been changed. Note this composes with the existing slow-circuit trip (a channel past CHANNEL_HEALTH_SLOW_LATENCY, 9s, is dropped from selection outright): the boost governs the still-selectable 2-9s band and whatever remains after trips. Tests fail when the boost is removed: the fast channel's share falls from >0.8 to ~0.59. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
修改数据看板文字