Skip to content

Conversation

@Starrah
Copy link
Contributor

@Starrah Starrah commented Jan 28, 2026

2025年挖过的坑,现在开始填。
Ready for review

  • 对现有的,仅支持MA2格式的replaceChart功能进行重构
  • 复用 ImportChartCheckApi ,这样在替换maidata.txt谱面前,也去做和导入新的类似的语法检查
  • 导入新谱面时会把导入模式存在XML的自定义字段中
  • 使 ReplaceChartModal 可以显示上述语法检查结果,和选择导入模式
    • 导入模式会尝试从XML中读取,读到了就不用用户选了,没读到(即老版本已经存在的谱面)则需要用户选择
  • 修改后端ReplaceChartApi,支持导入maidata(大概在此之前需要对ImportChart进行些小重构、逻辑通用的部分抽取出来之类的)

我知道这个改动有点大了(你看我就说这不是一个多简单的事对吧,牵涉的东西还挺多的),所以review的时候建议分commit review。比如有一个commit看起来有四五百行的+-,但其实因为是重构,只是把代码移动了一个地方,内容没有变。所以分commit 一个一个review可能会看的清楚一点。

…是普通的导入谱面功能发起的。

2. ReplaceChart新增参数shift,仅在导入的是MA2时需要传入,表示延迟调整模式。
3. 并进一步重构 ReplaceChartModal.tsx,确保原来的直接替换MA2功能可用的同时,预留新功能的空间。
@gemini-code-assist
Copy link

Summary of Changes

Hello @Starrah, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在扩展现有谱面替换功能,使其能够处理maidata.txt格式的谱面,而不仅仅是MA2格式。通过复用导入时的语法检查逻辑,确保替换的maidata.txt谱面质量。此外,还引入了将导入模式存储到音乐XML元数据中的机制,并对前端UI进行了调整,以更好地支持不同文件类型的替换流程,并为用户提供了关于替换功能适用范围的明确警告。

Highlights

  • 谱面替换功能重构: 重构了现有的谱面替换功能,使其不仅支持MA2格式,还初步支持maidata.txt格式的谱面替换。
  • 引入语法检查: 在替换maidata.txt谱面之前,复用了ImportChartCheckApi进行语法检查,确保替换的谱面符合规范。
  • 导入模式存储: 在导入新谱面时,会将导入模式(ShiftMethod)存储到XML的自定义字段中,以便后续读取和使用。
  • 前端交互优化: 前端界面现在能够区分处理MA2和maidata.txt文件,并为maidata.txt文件显示检查模态框,同时新增了“替换谱面”按钮。
  • MA2谱面音符数量修正: 替换MA2谱面时,会自动检查并修正谱面中的音符总数(MaxNotes)。
  • XML节点操作简化: 引入了SelectSingleNodeOrCreate辅助方法,简化了XML文档中节点的存在性检查和创建逻辑。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This PR aims to refactor the replaceChart functionality to support maidata.txt charts, enhancing both backend API logic and frontend UI. However, a security audit revealed two significant vulnerabilities in ChartController.cs: a Denial of Service (DoS) risk from reading large files into memory without validation, and a Path Traversal vulnerability allowing arbitrary file overwrites due to unsanitized assetDir parameters. Addressing these security issues is critical. Additionally, there are opportunities to improve the efficiency and robustness of backend file processing.

Comment on lines 98 to 99
public void ReplaceChart(int id, int level, IFormFile file, string assetDir,
[FromForm] ImportChartController.ShiftMethod? shift)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The assetDir parameter, which is a route parameter and thus user-controlled, is used to construct file paths using Path.Combine (e.g., on line 106) without any validation or sanitization. An attacker can provide a path containing directory traversal sequences (e.g., ..) to escape the intended directory and overwrite arbitrary files on the server where the application has write permissions.

Remediation: Validate the assetDir parameter against an allowlist of expected directory names or sanitize it to remove any path traversal sequences before using it in file system operations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@clansty 这个用管吗😰 虽然他说有问题的这段并不是我写的,我只是加了个shift参数而已(

string fileContent;
using (var reader = new StreamReader(file.OpenReadStream()))
{
fileContent = reader.ReadToEnd();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The ReplaceChart method at this line reads the entire content of an uploaded file into memory using reader.ReadToEnd() without any size validation. This creates a Denial of Service (DoS) risk, as a large file could lead to an OutOfMemoryException and application crash, especially when passed to ParseTNumAllFromMa2. Additionally, the current implementation has two code quality issues: it attempts to read the file stream multiple times (file.CopyTo(stream) then file.OpenReadStream()), which can cause errors with non-seekable streams, and it includes a redundant stream.Close() within a using statement. It is recommended to implement a maximum file size limit, process files using a stream-based approach, and refactor to read the file content only once for both writing and parsing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个不用太管吧,后端是在用户自己的电脑上运行的,我DoS我自己吗?

方便之后可以直接复用ImportMaidata函数实现替换。
但是这里的问题是ImportMaidata无法是静态的(因为里面有大量调用到的函数不是静态的,甚至还有一个logger是WebServer框架提供的)。因此后续还需要二阶段重构。
…ortChartController调用这个Service。

这样,MaidataImportService便可以被其他地方如ReplaceChart复用。
…ch的自动sync功能。

如ReplaceChart等后端接口,可能会涉及对MusicXml中的信息进行修改后保存。此时前端updateMusicList时会发现相关数据出现变更,触发了MusicEdit/ChartPanel中的watch,造成发送了多个多余的edit请求、同时modified也被错误设置为true。
因此,对于涉及内部对xml进行修改后会自动保存的后端接口,可以在updateMusicList期间打开本选项,以阻止MusicEdit/ChartPanel中的sync动作。
@Starrah Starrah changed the title [WIP, NOT ready for review] 直接替换maidata.txt谱面的功能 [Ready for review] 直接替换maidata.txt谱面的功能 Feb 1, 2026
@Starrah Starrah marked this pull request as ready for review February 1, 2026 04:01
@Starrah
Copy link
Contributor Author

Starrah commented Feb 1, 2026

/gemini summary

@Starrah
Copy link
Contributor Author

Starrah commented Feb 1, 2026

/gemini review

@gemini-code-assist
Copy link

Summary of Changes

此拉取请求显著增强了谱面管理器的功能,允许用户直接替换maidata.txt格式的谱面,而不再局限于MA2格式。通过将复杂的谱面导入和解析逻辑封装到独立的MaidataImportService中,并复用现有的语法检查API,确保了替换过程的健壮性和数据一致性。前端界面也进行了全面更新,以支持新的文件类型,提供详细的检查结果反馈,并智能地处理导入模式的选择,从而为用户提供了更灵活、更安全的谱面替换体验。

Highlights

  • 重构谱面替换功能: 现有仅支持MA2格式的谱面替换功能已重构,现在支持直接替换maidata.txt格式的谱面。
  • 引入谱面语法检查: 在替换maidata.txt谱面之前,复用了ImportChartCheckApi进行语法检查,确保替换的谱面符合规范。
  • 存储导入模式: 导入新谱面时,会将导入模式(ShiftMethod)存储到音乐XML的自定义字段中,以便后续读取和使用。
  • 前端交互优化: ReplaceChartModal现在能够显示语法检查结果,并允许用户选择导入模式。如果XML中已存在导入模式,则会锁定该选项。
  • MA2谱面音符数量修正: 替换MA2谱面时,会自动检查并修正谱面中的音符总数(MaxNotes)。
  • XML节点操作简化: 引入了SelectSingleNodeOrCreate辅助方法,简化了XML文档中节点的存在性检查和创建逻辑。
  • 引入MaidataImportService: 将maidata导入和谱面解析的核心逻辑封装到新的MaidataImportService中,提高了代码的模块化和可维护性。
  • 拖放支持maidata.txt: 拖放功能现在支持直接拖放maidata.txt文件进行谱面替换。
  • 防止意外同步: 引入disableSync机制,在后端接口修改MusicXml并自动保存时,防止前端组件触发不必要的同步操作。
Changelog
  • MaiChartManager/Controllers/Charts/ChartController.cs
    • 引入MaidataImportService依赖。
    • ReplaceChart方法签名更新,现在返回ImportChartResult并接受ShiftMethod参数。
    • ReplaceChart方法逻辑重构,根据文件扩展名(.ma2或maidata.txt)分别处理。
    • 对于.ma2文件,新增了自动修正MaxNotes的逻辑。
    • 对于maidata.txt文件,调用MaidataImportService进行导入处理。
    • 增加了对不支持文件格式的错误处理。
  • MaiChartManager/Controllers/Charts/ImportChartController.cs
    • 引入MaidataImportService依赖。
    • 将大量与谱面解析和处理相关的私有方法(如MessageLevel、ShiftMethod、TryParseChartSimaiSharp等)迁移至MaidataImportService。
    • ImportChartCheck方法新增isReplacement参数,并在替换场景下添加警告信息。
    • ImportChartCheck和ImportChart方法现在调用MaidataImportService中的相应逻辑。
  • MaiChartManager/Front/src/client/apiGen.ts
    • MusicXmlWithABJacket接口新增shiftMethod字段。
    • ReplaceChart API调用现在返回ImportChartResult并接受shift参数。
    • ImportChartCheck API调用新增isReplacement参数。
  • MaiChartManager/Front/src/components/DragDropDispatcher/ReplaceChartModal.tsx
    • 重构了谱面替换的模态框逻辑,现在可以处理.ma2和maidata.txt文件。
    • 对于maidata.txt文件,会先进行语法检查并显示结果,同时提供导入模式选择器。
    • 如果音乐XML中已记录shiftMethod,则导入模式选择器会被锁定。
    • 替换成功或失败后,会显示相应的对话框。
    • 引入CheckingModal、LevelTagsDisplay、ImportAlert和ShiftModeSelector组件。
  • MaiChartManager/Front/src/components/DragDropDispatcher/index.tsx
    • 更新拖放处理逻辑,使其能够识别并处理maidata.txt文件,并调用新的prepareReplaceChart函数。
  • MaiChartManager/Front/src/components/ImportCreateChartButton/ImportChartButton/ErrorDisplayIdInput.tsx
    • 将错误和警告信息的显示逻辑抽象为ImportAlert组件。
    • 将延迟调整模式选择器抽象为ShiftModeSelector组件。
  • MaiChartManager/Front/src/components/ImportCreateChartButton/ImportChartButton/ImportAlert.tsx
    • 新增文件,用于统一显示谱面导入或替换过程中的信息、警告和错误。
  • MaiChartManager/Front/src/components/ImportCreateChartButton/ImportChartButton/ShiftModeSelector.tsx
    • 新增文件,提供一个可复用的组件,用于选择谱面导入的延迟调整模式,并支持锁定状态。
  • MaiChartManager/Front/src/components/ImportCreateChartButton/ImportChartButton/types.ts
    • TempOptions类型新增shiftLocked属性,用于控制导入模式选择器的锁定状态。
  • MaiChartManager/Front/src/components/LevelTagsDisplay.tsx
    • 新增文件,用于显示音乐的难度标签。
  • MaiChartManager/Front/src/components/MusicEdit/ChartPanel.tsx
    • 在谱面编辑面板中新增“替换谱面”按钮,调用prepareReplaceChart功能。
    • 修改了同步逻辑,在disableSync为true时阻止同步操作。
  • MaiChartManager/Front/src/components/MusicEdit/index.tsx
    • 引入disableSync状态,用于在特定操作期间禁用前端的自动同步机制。
  • MaiChartManager/Front/src/components/MusicList/MusicEntry.tsx
    • 使用LevelTagsDisplay组件替换了原有的难度标签显示逻辑。
  • MaiChartManager/Front/src/locales/en.yaml
    • 新增了多条本地化字符串,包括文件类型验证、替换谱面时的提示和导入模式锁定提示等。
  • MaiChartManager/Front/src/locales/zh-TW.yaml
    • 新增了多条本地化字符串,包括文件类型验证、替换谱面时的提示和导入模式锁定提示等。
  • MaiChartManager/Front/src/locales/zh.yaml
    • 新增了多条本地化字符串,包括文件类型验证、替换谱面时的提示和导入模式锁定提示等。
  • MaiChartManager/Locale.Designer.cs
    • 新增了NotesReplacementWarning本地化字符串。
  • MaiChartManager/Locale.resx
    • 新增了NotesReplacementWarning本地化字符串。
  • MaiChartManager/Locale.zh-hans.resx
    • 新增了NotesReplacementWarning本地化字符串。
  • MaiChartManager/Locale.zh-hant.resx
    • 新增了NotesReplacementWarning本地化字符串。
  • MaiChartManager/Models/MusicXml.cs
    • 新增SelectSingleNodeOrCreate私有方法,用于简化XML节点的存在性检查和创建。
    • UtageKanji和Comment属性的设置器现在使用SelectSingleNodeOrCreate。
    • 新增ShiftMethod属性,用于在XML中存储和读取谱面的导入模式,路径为X-MCM/shiftMethod。
  • MaiChartManager/ServerManager.cs
    • 在依赖注入容器中注册MaidataImportService为单例服务。
  • MaiChartManager/Services/MaidataImportService.cs
    • 新增文件,封装了所有与maidata导入相关的核心业务逻辑,包括:
    • 定义了MessageLevel、ImportChartMessage、ImportChartResult和ShiftMethod等数据结构。
    • 包含了用于解析和修正谱面文本的正则表达式和辅助方法。
    • 提供了TryParseChartSimaiSharp和TryParseChart方法用于谱面解析。
    • 实现了CalcMusicPadding和ParseTNumAllFromMa2等计算方法。
    • 核心的ImportMaidata方法,负责处理maidata.txt文件的完整导入流程,包括谱面解析、偏移调整、难度设定和文件写入。
Activity
  • gemini-code-assist[bot]在2026年1月28日提供了PR的初步总结和代码审查意见。
  • gemini-code-assist[bot]指出assetDir参数存在目录遍历漏洞风险,建议进行验证或清理。
  • gemini-code-assist[bot]指出ReplaceChart方法在读取大文件时可能导致DoS风险,建议限制文件大小并优化文件读取方式。
  • gemini-code-assist[bot]建议ma2Content.Split('\n')对换行符的处理不够健壮,建议使用ReplaceLineEndings().Split('\n')。
  • gemini-code-assist[bot]建议移除调试用的console.log语句。
  • Starrah在2026年1月28日回应了gemini-code-assist[bot]关于assetDir的评论,询问是否需要处理,并指出该代码并非自己所写。
  • Starrah在2026年1月28日回应了gemini-code-assist[bot]关于DoS风险的评论,认为后端在用户本地运行,DoS风险不重要。
  • Starrah在2026年1月28日回应了gemini-code-assist[bot]关于换行符的评论,认为Trim操作可以处理。
  • Starrah在2026年2月1日再次请求了/gemini summary和/gemini review。

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This Pull Request successfully refactors the chart replacement function to support maidata.txt files, enhancing chart checking through ImportChartCheckApi reuse and improving maintainability by extracting core import logic into MaidataImportService. UI components like ImportAlert and ShiftModeSelector have been effectively split, and storing ShiftMethod in XML custom fields is a good design. However, a critical path traversal vulnerability has been introduced. The assetDir parameter in the chart replacement and import logic lacks proper validation, potentially allowing attackers to write files to arbitrary server locations. Strict validation for the assetDir parameter is required to address this. Minor improvements are also suggested in ChartController.cs. Overall, this is a high-quality submission, but the security vulnerability needs immediate attention.

{
var targetChart = music.Charts[level];
targetChart.Path = $"{id:000000}_0{level}.ma2";
using var stream = System.IO.File.Open(Path.Combine(StaticSettings.StreamingAssets, assetDir, "music", $"music{id:000000}", targetChart.Path), FileMode.Create);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The assetDir parameter is used to construct a file path without validation. An attacker could provide a value like ../../.. to perform a path traversal attack and write files outside the intended directory. This is especially dangerous here as the content of the written file is controlled by the user upload. Consider validating assetDir against an allow-list of expected directory names.

// Fallback to maiLibChart if T_NUM_ALL not found
if (targetChart.MaxNotes == 0) targetChart.MaxNotes = maiLibChart.AllNoteNum;

File.WriteAllText(Path.Combine(Path.GetDirectoryName(music.FilePath)!, targetChart.Path), shiftedConverted);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The music.FilePath property, which is derived from the user-controlled assetDir parameter, is used to determine the directory where the chart file is written. This allows for a path traversal attack where an attacker can overwrite arbitrary files on the system by providing a malicious assetDir value. This issue affects multiple File.WriteAllText calls in this method (e.g., lines 363, 364, 365, 398, 401).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant