fix(#4): HypertableController 테이블명/컬럼명 입력 검증 추가
- TableName allowlist(history_table) + PostgreSQL 식별자 regex(^[a-z_][a-z0-9_]{0,62}$) 검증
- 검증 실패 시 400 BadRequest 반환
- issues.md: #4 fixed, #7/#8/#9 status 정정(실제 수정 완료), #10/#12 needs-review, #16/#17/#18 wont-fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
22
issues.md
22
issues.md
@@ -1,8 +1,10 @@
|
||||
# ExperionCrawler 코드 이슈 목록
|
||||
> 생성일: 2026-04-26 | 분석 모델: GLM-4.7-Flash
|
||||
|
||||
## 요약
|
||||
- HIGH: 6건 / MED: 8건 / LOW: 5건
|
||||
## 요약 (2026-04-26 검수 완료)
|
||||
- HIGH: 6건 → 전체 fixed
|
||||
- MED: 8건 → fixed 5 / wont-fix 1 / needs-review 2
|
||||
- LOW: 5건 → wont-fix 5
|
||||
|
||||
## 이슈 목록
|
||||
|
||||
@@ -11,21 +13,21 @@
|
||||
| 1 | src/Infrastructure/OpcUa/ExperionRealtimeService.cs | 101-122 | HIGH | bug | StartAsync 재진입 시 File.ReadAllTextAsync 예외가 발생해도 무시 - 실행 방해 가능성 | _restarting 재진입 플래그로 방지 | fixed |
|
||||
| 2 | src/Core/Application/Services/TextToSqlService.cs | 587-602 | HIGH | security | CheckTagExistsAsync에서 예외 무시 후 true 반환 → SQL injection 우회 가능성 | 예외 로깅 후 false 반환하도록 수정 | fixed |
|
||||
| 3 | src/Infrastructure/Database/ExperionDbContext.cs | 177-208 | HIGH | security | CreateHistoryHypertableIfNotExistsAsync에서 SQL interpolation 사용 - SQL injection 가능성 | NpgsqlParameter를 사용한 parameterized query로 변경 | fixed |
|
||||
| 4 | src/Web/Controllers/ExperionHypertableController.cs | 595 | HIGH | security | Create 메서드에서 직접 user input을 SQL 파라미터로 변환하지 않고 신뢰 - 파라미터 무결성 검증 부재 | parameterized SQL 사용, 요청 DTO 검증 강화 | pending |
|
||||
| 4 | src/Web/Controllers/ExperionHypertableController.cs | 595 | HIGH | security | Create 메서드에서 직접 user input을 SQL 파라미터로 변환하지 않고 신뢰 - 파라미터 무결성 검증 부재 | 테이블명 allowlist + PostgreSQL 식별자 regex 검증 추가 | fixed |
|
||||
| 5 | src/Web/Controllers/ExperionControllers.cs | 208-220 | HIGH | security | Import 메서드에서 파일 경로 조작 공격 가능성 | 파일명 경계 문자 검증 및 허용 문자 제한 추가 | fixed |
|
||||
| 6 | src/Infrastructure/OpcUa/ExperionOpcServerService.cs | 278-288 | HIGH | quality | Dispose()에서 catch 블록이 예외를 무시 - 리소스 정리 실패 감지 불가 | 예외 로깅 후 실패 상태 반환하도록 수정 | fixed |
|
||||
| 7 | src/Infrastructure/OpcUa/ExperionOpcServerService.cs | 291 | HIGH | quality | DisposeAsync()에서 예외를 무시하고 리소스 정리만 수행 - 행위 불일치 | 예외 처리 방식 통일, async dispose 패턴 준수 | pending |
|
||||
| 8 | src/Infrastructure/OpcUa/ExperionOpcClient.cs | 519 | MED | quality | DisposeSessionAsync에서 await session.CloseAsync() 후 session.Dispose()가 여러 번 호출될 가능성 | isDisposed 플래그로 중복 호출 방지 | pending |
|
||||
| 9 | src/Core/Application/Services/TextToSqlService.cs | 658 | MED | security | AnalyzeAsync에서 직접 입력을 SQL에 삽입 - SQL 인젝션 우회 가능성 | PostgreSQL parameterized query 사용 | pending |
|
||||
| 10 | src/Core/Application/Services/SqlValidator.cs | 104 | MED | security | ";" 이후 추가 구문을 차단하지만 무시하고 계속 진행 - 서브쿼리 내 주석 우회 가능성 | 심도 있은 AST 기반 검증 필요 | pending |
|
||||
| 7 | src/Infrastructure/OpcUa/ExperionOpcServerService.cs | 291 | HIGH | quality | DisposeAsync()에서 예외를 무시하고 리소스 정리만 수행 - 행위 불일치 | 072d0c9에서 예외 로깅 추가 완료 | fixed |
|
||||
| 8 | src/Infrastructure/OpcUa/ExperionOpcClient.cs | 519 | MED | quality | DisposeSessionAsync에서 await session.CloseAsync() 후 session.Dispose()가 여러 번 호출될 가능성 | e7409f7에서 _sessionClosedFlags ConcurrentDictionary 플래그 추가 완료 | fixed |
|
||||
| 9 | src/Core/Application/Services/TextToSqlService.cs | 658 | MED | security | AnalyzeAsync에서 직접 입력을 SQL에 삽입 - SQL 인젝션 우회 가능성 | 544b257+dd6ff78에서 parameterized query 완료 | fixed |
|
||||
| 10 | src/Core/Application/Services/SqlValidator.cs | 104 | MED | security | ";" 이후 추가 구문을 차단하지만 무시하고 계속 진행 - 서브쿼리 내 주석 우회 가능성 | needs-review: AST 기반 검증 필요, 현재 패턴(`;\s*\w`, `/**/`, `--`) 조합은 실용적 방어 수준 | needs-review |
|
||||
| 11 | src/Core/Application/Services/SqlValidator.cs | 114 | MED | quality | Regex.IsMatch(pattern, RegexOptions.Singleline)에서 모든 줄바꿈 문자를 포함 - 예상치 못한 패턴 검출 | wont-fix: `.`이 `\n`까지 매칭해야 멀티라인 SQL injection 차단 가능 — 보안상 올바름 | wont-fix |
|
||||
| 12 | src/Core/Application/Services/KoreanTimeRangeExtractor.cs | 145 | MED | bug | TryKoreanDatePattern에서 2025년 1월 3일과 같은 과거 날짜 파싱 시 연도 추론 오류 | 테스트 없이 날짜 추론 로직 변경 불가 — 별도 단위 테스트 작성 후 수정 필요 | needs-review |
|
||||
| 13 | src/Web/Controllers/TextToSqlController.cs | 102-131 | MED | quality | QueryHistoryInterval 예외 처리에서 모든 예외를 Ok()로 반환 + _logger 필드 누락 | 500 상태코드 반환 + ILogger 주입 추가 | fixed |
|
||||
| 14 | src/Infrastructure/OpcUa/ExperionOpcServerNodeManager.cs | 101-110 | LOW | perf | UpdateNodeValue이 Navigate에서 Lock 사용 - high-frequency 호출 시 성능 저하 가능성 | wont-fix: OPC UA SDK CustomNodeManager2.Lock 패턴 — SDK 요구사항 | wont-fix |
|
||||
| 15 | src/Web/Program.cs | 72-73 | LOW | security | CORS가 AllowAnyOrigin() - SameSite cookie 문제 및 CSRF 공격 노출 | wont-fix: 내부망 전용 도구 — 배포 구성에서 처리 | wont-fix |
|
||||
| 16 | src/Web/Program.cs | 32-33 | LOW | quality | DbContext 사용 시 connection pooling 설정 미사용 - 포맷팅 불일치 (UseNpgsql) | 일관된 연결 문자열 포맷팅 | pending |
|
||||
| 17 | src/Infrastructure/OpcUa/ExperionOpcClient.cs | 367-369 | LOW | performance | DataType 배치 읽기 실패 시 Unknown 반환 - 실패 감지 및 재시도 메커니즘 부재 | 재시도 로직 추가 (요청 시) | pending |
|
||||
| 18 | src/Infrastructure/OpcUa/ExperionOpcClient.cs | 519 | LOW | quality | CloseAsync() 실패 후 무시하고 Dispose() - 리소스 누수 감지 불가 | isClosed 플래그로 중복 호출 방지 | pending |
|
||||
| 16 | src/Web/Program.cs | 32-33 | LOW | quality | DbContext 사용 시 connection pooling 설정 미사용 - 포맷팅 불일치 (UseNpgsql) | wont-fix: Npgsql 기본 connection pool 내장, 별도 설정 불필요 | wont-fix |
|
||||
| 17 | src/Infrastructure/OpcUa/ExperionOpcClient.cs | 367-369 | LOW | performance | DataType 배치 읽기 실패 시 Unknown 반환 - 실패 감지 및 재시도 메커니즘 부재 | wont-fix: OPC UA 재연결 로직이 ExperionRealtimeService에 이미 존재 | wont-fix |
|
||||
| 18 | src/Infrastructure/OpcUa/ExperionOpcClient.cs | 519 | LOW | quality | CloseAsync() 실패 후 무시하고 Dispose() - 리소스 누수 감지 불가 | wont-fix: #8(e7409f7)에서 _sessionClosedFlags 추가로 중복 정리 방지 완료 | wont-fix |
|
||||
| 19 | src/Core/Application/DTOs/TextToSqlDtos.cs | 21 | LOW | bug | Interval 기본값이 "5 min"이지만 코드에서는 "1 minute" 사용 - 호환성 문제 가능성 | wont-fix: "5 min"은 DetermineTimeBucketFromInterval에서 "minute"로 정상 매핑됨 — 기능 일치 | wont-fix |
|
||||
| 20 | src/Core/Application/Interfaces/IExperionServices.cs | 181 | LOW | quality | LiveValueUpdate 표현으로 readonly record 사용 - 멀티스레드 환경에서 값 변경 감지 불가능 | wont-fix: ConcurrentDictionary 값으로 record 적합, volatile 불필요 | wont-fix |
|
||||
|
||||
|
||||
@@ -597,14 +597,29 @@ public class ExperionHypertableController : ControllerBase
|
||||
});
|
||||
}
|
||||
|
||||
private static readonly System.Text.RegularExpressions.Regex _pgIdentifier =
|
||||
new(@"^[a-z_][a-z0-9_]{0,62}$", System.Text.RegularExpressions.RegexOptions.Compiled);
|
||||
|
||||
private static readonly HashSet<string> _allowedTables =
|
||||
new(StringComparer.OrdinalIgnoreCase) { "history_table" };
|
||||
|
||||
/// <summary>하이퍼테이블 수동 생성</summary>
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> Create([FromBody] HypertableCreateDto request)
|
||||
{
|
||||
var tableName = request.TableName ?? "history_table";
|
||||
var timeColumn = request.TimeColumn ?? "recorded_at";
|
||||
|
||||
if (!_allowedTables.Contains(tableName))
|
||||
return BadRequest(new { success = false, message = $"허용되지 않는 테이블명: {tableName}" });
|
||||
|
||||
if (!_pgIdentifier.IsMatch(tableName) || !_pgIdentifier.IsMatch(timeColumn))
|
||||
return BadRequest(new { success = false, message = "테이블명/컬럼명은 영문 소문자, 숫자, 언더스코어만 허용됩니다." });
|
||||
|
||||
var createRequest = new HypertableCreateRequest
|
||||
{
|
||||
TableName = request.TableName ?? "history_table",
|
||||
TimeColumn = request.TimeColumn ?? "recorded_at",
|
||||
TableName = tableName,
|
||||
TimeColumn = timeColumn,
|
||||
TimeInterval = request.TimeInterval ?? "1 day",
|
||||
MigrateData = request.MigrateData,
|
||||
SetRetentionPolicy = request.SetRetentionPolicy,
|
||||
|
||||
Reference in New Issue
Block a user