Hotfix: strip invalid XML control chars from field values (FR-16)
ผู้ใช้ paste ข้อความที่มี vertical tab (0x0B) เข้าฟอร์ม — XML 1.0 ห้าม อักขระนี้ ทำให้ XmlWriter โยน ArgumentException ตอน save package และ หลุดเป็น 500 stack เต็มจอ (catch เดิมครอบแค่ OpenXmlPackageException) - DocxGenerationService.SanitizeForXml: ตัดอักขระที่ XML 1.0 ห้าม (0x00–0x1F ยกเว้น tab/LF/CR, lone surrogate, 0xFFFE/0xFFFF — คู่ surrogate ครบ เช่น อีโมจิ คงไว้) ที่จุดเขียนค่าทั้งสองของ ReplaceSdtContent → ครอบทั้ง generate และ generate-draft - Controllers: broad catch (ยกเว้น cancellation) → 502 ข้อความอ่านออก แทน 500 ดิบ สำหรับ exception ที่ไม่ได้ตั้งใจจาก OpenXML - Test: Draft_ValueWithInvalidXmlControlChars_IsStrippedNot500 (draft + generate v0) — reviewer พิสูจน์แล้วว่า fail จริงบนโค้ดก่อนแก้ - backend 85/85; QA reviewer PASS (1 round, minors 3: แก้ 1 ปฏิเสธ 2 พร้อมเหตุผล — บันทึกใน acceptance-review.md) Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
70f71f4c5c
commit
b9b227926d
8 changed files with 96 additions and 7 deletions
|
|
@ -89,7 +89,8 @@ public sealed class DraftTests(IntegrationTestFixture fixture)
|
|||
}
|
||||
|
||||
/// <summary>สร้าง template จาก BuildThaiTemplateDocx พร้อมตั้ง field defs (detail1 required, remark1 ทางเลือก)</summary>
|
||||
private async Task<Guid> CreateThaiTemplateAsync() {
|
||||
private async Task<Guid> CreateThaiTemplateAsync()
|
||||
{
|
||||
using var client = fixture.CreateClient();
|
||||
using var create = await client.PostAsync(
|
||||
"api/templates",
|
||||
|
|
@ -191,6 +192,35 @@ public sealed class DraftTests(IntegrationTestFixture fixture)
|
|||
Assert.Equal(templateId.ToString(), TestDocx.ReadCustomProperty(fileBytes, AddInMetadata.TemplateIdPropertyName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Draft_ValueWithInvalidXmlControlChars_IsStrippedNot500()
|
||||
{
|
||||
// ผู้ใช้ paste ข้อความที่มีอักขระควบคุม (vertical tab 0x0B / form feed 0x0C) มาในค่า field —
|
||||
// XML 1.0 ห้ามอักขระพวกนี้ เดิม XmlWriter โยน ArgumentException ตอน save ทั้ง package กลายเป็น 500
|
||||
// ต้องตัดออกเงียบๆ แล้วสร้างไฟล์สำเร็จ (อักขระมองไม่เห็นอยู่แล้ว) — ครอบทั้ง draft และ generate v0
|
||||
using var client = fixture.CreateClient();
|
||||
var (templateId, _) = await TestFlow.CreateConfiguredTemplateAsync(client, "paste control chars");
|
||||
var values = new Dictionary<string, string>(TestFlow.DefaultValues)
|
||||
{
|
||||
["position"] = "วิศวกร\u000Bซอฟต์\u000Cแวร์", // VT/FF ฝังเป็น escape กันอักขระล่องหนหายระหว่างแก้ไขไฟล์
|
||||
};
|
||||
|
||||
// ไฟล์ร่าง
|
||||
using var draft = await client.PostAsJsonAsync(
|
||||
$"api/templates/{templateId}/generate-draft", new { values }, IntegrationTestFixture.Json);
|
||||
Assert.Equal(HttpStatusCode.OK, draft.StatusCode);
|
||||
var link = (await draft.Content.ReadFromJsonAsync<DraftLinkResult>(IntegrationTestFixture.Json))!;
|
||||
using var browser = new HttpClient();
|
||||
var draftBytes = await (await browser.GetAsync(link.Url)).Content.ReadAsByteArrayAsync();
|
||||
Assert.Equal("วิศวกรซอฟต์แวร์", TestDocx.ReadSdtText(draftBytes, "position"));
|
||||
|
||||
// generate v0 ใช้ ReplaceSdtContent ตัวเดียวกัน — ตรวจซ้ำจุดเดียวกันผ่านอีก endpoint
|
||||
var doc = await TestFlow.GenerateDocumentAsync(client, templateId, values);
|
||||
using var download = await client.GetAsync(doc.DownloadUrl);
|
||||
var docBytes = await download.Content.ReadAsByteArrayAsync();
|
||||
Assert.Equal("วิศวกรซอฟต์แวร์", TestDocx.ReadSdtText(docBytes, "position"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Draft_LifecycleRule_Idempotent_AndSyncsRetentionDays()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue