Skip to content

Commit d4f8631

Browse files
committed
security: ChatSegment 소유권 이전 검증 및 IDisposable 패턴 강화
record를 sealed class로 변경하여 'with' 복제 시 메모리 참조 공유 방지 AudioMemoryOwner 접근성을 internal로 제한하여 외부 직접 접근 차단 WithAudioMemory 메서드에서 기존 소유자 자동 해제로 메모리 누수 방지 중복 Dispose 방지를 위한 명시적 소유권 이전 구현
1 parent ace9e1a commit d4f8631

File tree

1 file changed

+26
-13
lines changed

1 file changed

+26
-13
lines changed

ProjectVG.Application/Models/Chat/ChatSegment.cs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,24 @@
33

44
namespace ProjectVG.Application.Models.Chat
55
{
6-
public record ChatSegment : IDisposable
6+
public sealed class ChatSegment : IDisposable
77
{
88

9-
public string Content { get; init; } = string.Empty;
9+
public string Content { get; private set; } = string.Empty;
1010

11-
public int Order { get; init; }
11+
public int Order { get; private set; }
1212

13-
public string? Emotion { get; init; }
13+
public string? Emotion { get; private set; }
1414

15-
public List<string>? Actions { get; init; }
15+
public List<string>? Actions { get; private set; }
1616

17-
public byte[]? AudioData { get; init; }
18-
public string? AudioContentType { get; init; }
19-
public float? AudioLength { get; init; }
17+
public byte[]? AudioData { get; private set; }
18+
public string? AudioContentType { get; private set; }
19+
public float? AudioLength { get; private set; }
2020

2121
// 스트림 기반 음성 데이터 처리를 위한 새로운 프로퍼티
22-
public IMemoryOwner<byte>? AudioMemoryOwner { get; init; }
23-
public int AudioDataSize { get; init; }
22+
internal IMemoryOwner<byte>? AudioMemoryOwner { get; private set; }
23+
internal int AudioDataSize { get; private set; }
2424

2525

2626

@@ -50,6 +50,8 @@ public ReadOnlySpan<byte> GetAudioSpan()
5050

5151

5252

53+
private ChatSegment() { }
54+
5355
public static ChatSegment Create(string content, string? emotion = null, List<string>? actions = null, int order = 0)
5456
{
5557
return new ChatSegment
@@ -74,8 +76,12 @@ public static ChatSegment CreateAction(string action, int order = 0)
7476
// Method to add audio data (returns new record instance)
7577
public ChatSegment WithAudioData(byte[] audioData, string audioContentType, float audioLength)
7678
{
77-
return this with
79+
return new ChatSegment
7880
{
81+
Content = this.Content,
82+
Order = this.Order,
83+
Emotion = this.Emotion,
84+
Actions = this.Actions,
7985
AudioData = audioData,
8086
AudioContentType = audioContentType,
8187
AudioLength = audioLength
@@ -104,8 +110,15 @@ public ChatSegment WithAudioMemory(IMemoryOwner<byte> audioMemoryOwner, int audi
104110
audioDataSize,
105111
$"audioDataSize는 0 이상 {audioMemoryOwner.Memory.Length} 이하여야 합니다.");
106112

107-
return this with
113+
// 기존 AudioMemoryOwner가 있다면 해제 (소유권 이전)
114+
this.AudioMemoryOwner?.Dispose();
115+
116+
return new ChatSegment
108117
{
118+
Content = this.Content,
119+
Order = this.Order,
120+
Emotion = this.Emotion,
121+
Actions = this.Actions,
109122
AudioMemoryOwner = audioMemoryOwner,
110123
AudioDataSize = audioDataSize,
111124
AudioContentType = audioContentType,
@@ -147,7 +160,7 @@ public void Dispose()
147160
/// 보호된 Dispose 패턴 구현
148161
/// </summary>
149162
/// <param name="disposing">관리되는 리소스를 해제할지 여부</param>
150-
protected virtual void Dispose(bool disposing)
163+
private void Dispose(bool disposing)
151164
{
152165
if (disposing && AudioMemoryOwner != null)
153166
{

0 commit comments

Comments
 (0)