Files
ExperionCrawler/issues.md
windpacer 77bdcf1f7f feat: ExperionCrawler IIoT OPC UA Data Bridge Infrastructure
Major project initialization and feature implementation:

**Core Features:**
- OPC UA client for Honeywell Experion HS R530 integration
- Real-time data streaming and history data retrieval
- Text-to-SQL query engine with TimeScaleDB
- JSON-based node configuration system
- SQLite database with migration support

**Architecture:**
- Clean architecture with Domain, Application, Infrastructure layers
- ASP.NET Core Web API frontend
- Web UI with real-time visualization
- PKI-based OPC UA authentication (TLS)

**Infrastructure Components:**
- ExperionOpcClient: OPC UA connection management
- ExperionRealtimeService: Real-time data streaming
- ExperionHistoryService: Historical data queries
- TextToSqlService: Natural language to SQL queries
- SqlValidator: SQL injection prevention

**Database:**
- TimescaleDB integration (recommended) or SQLite fallback
- Entity Framework Core with Extenstion methods
- OPCTag, KeyValue tables for data storage

**Security:**
- Certificate-based OPC UA endpoint security
- SSL/TLS encryption for database connections
- Output param binding injection prevention

**Testing:**
- Unit tests for TextToSqlService and SqlValidator
- Integration tests for Korean time range extraction

See REVIEW_REQUEST.md for detailed code review information.
2026-04-26 19:28:56 +09:00

9.7 KiB

ExperionCrawler 코드 이슈 목록

생성일: 2026-04-26 | 분석 모델: GLM-4.7-Flash

요약 (2026-04-26 검수 완료)

  • HIGH: 6건 → 전체 fixed
  • MED: 8건 → fixed 5 / wont-fix 1 / needs-review 2
  • LOW: 5건 → wont-fix 5

아키텍처 노트: ExperionHypertableController(#3, #4)와 관련 DbContext 메서드는 레거시 관리 도구입니다. 앱 코어 경로는 history_table을 직접 조회(time_bucket 미사용)하도록 변경되어 있으며, 이 컨트롤러는 history_table → TimescaleDB hypertable 전환이 필요할 때를 위한 one-time 관리 UI로 보존합니다.

이슈 목록

# 파일 라인 심각도 분류 문제 설명 수정 방향 상태
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로 변경 — ※ 레거시 관리 도구 (앱 시작 시 미호출, UI 수동 트리거 전용) fixed
4 src/Web/Controllers/ExperionHypertableController.cs 595 HIGH security Create 메서드에서 직접 user input을 SQL 파라미터로 변환하지 않고 신뢰 - 파라미터 무결성 검증 부재 테이블명 allowlist + PostgreSQL 식별자 regex 검증 추가 — ※ 레거시 관리 도구 (history_table을 TimescaleDB hypertable로 전환하는 one-time 도구) 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()에서 예외를 무시하고 리소스 정리만 수행 - 행위 불일치 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) 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

상세 분석

HIGH 우선순위 이슈

#1 ExperionRealtimeService.cs:120 - 재진입 예외 처리 누락

if (cfg != null)
{
    _logger.LogInformation("[Realtime] 자동 재시작 플래그 감지 — 구독 자동 시작");
    await StartAsync(cfg);  // 재귀 호출 시 예외 발생 시 Unknown
}

문제: 재진입 시 파일 읽기 예외가 발생해도 무시됨 수정: 예외 로깅 후 safe fallback 처리 추가

#2 TextToSqlService.cs:601 - 예외 무시 → SQL injection 위험

catch
{
    // 확인 실패 시 true 반환 (쿼리 실행 시 에러 처리)
    return true;
}

문제: 태그 존재 여부 확인 실패 시 억지로 true 반환 → 태그가 없는 데이터 조회 → SQL injection 우회될 수 있음 수정: 예외 로깅 후 false 반환

#3 ExperionDbContext.cs:216 - SQL interpolation

await _ctx.Database.ExecuteSqlRawAsync(
    $"SELECT create_hypertable('{tableName}', '{timeColumn}'::text, ...)");

문제: Interpolated string 사용 → SQL injection 위험 수정: NpgsqlParameter 사용 또는 매우 엄격한 식별자 검증

#4 ExperionHypertableController.cs:595 - 파라미터 검증 없음

var dbSvc = scope.ServiceProvider.GetRequiredService<IExperionDbService>();
await dbSvc.CreateHypertableAsync(createRequest);

문제: 요청 DTO는 검증되었지만 클라이언트는 JSON 생성자를 통해 직접 생성 가능 → 위조 요청 가능 수정: 클라이언트 검증 지원 또는 서버 측 DTO 검증 강화

#5 ExperionControllers.cs:212-213 - 파일 경로 조작

if (!string.IsNullOrEmpty(dto.ServerHostName) &&
    dto.FileName.StartsWith(dto.ServerHostName, StringComparison.OrdinalIgnoreCase))

문제: 파일 이름만 path traversal 검증 → 무의미한 파일 접근 방어 불가 수정: 전체 경로 점검 및 확장자 하드코딩

#6 ExperionOpcServerService.cs:286 - 예외 무시

catch { /* ignore */ }

문제: 리소스 정리 중 예외가 무시되어 문제 감지 불가 수정: 최소한 로그 기록 필요

MED 우선순위 이슈

#8 ExperionOpcClient.cs:519 - 세션 중복 해제

private static async Task DisposeSessionAsync(ISession? session)
{
    if (session == null) return;
    try { await session.CloseAsync(); } catch { /* ignore */ }
    session.Dispose();  // CloseAsync 실패 후에도 Dispose 호출
}

문제: 이미 close된 session에서 Dispose()가 호출될 경우 DuplicateOperationException 발생 가능 수정: isClosed 플래그 관리

#9 TextToSqlService.cs:658 - SQL injection

WHERE tagname = '{escapedTagName}' ...

문제: 단순히 작은따옴표 이스케이프만 수행 수정: PostgreSQL parameterized query 사용

#12 KoreanTimeRangeExtractor.cs:145 - 날짜 추론 오류

if (dt > KstToday.AddDays(1)) dt = dt.AddYears(-1);

문제: 2025년 1월 3일 (현재가 2026년 4월 26일)인 경우 올해가 아닌 작년으로 처리 → 날짜 추론 오류 수정: 경과 시간 계산 후 올바른 연도 계산

LOW 우선순위 이슈

#15 Program.cs:72-73 - CORS AllowAnyOrigin

opt.AddDefaultPolicy(p => p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());

문제: SameSite cookie 문제 및 CSRF 공격 노출 가능성 수정: Cross-Origin-Opener-Policy, Cross-Origin-Embedder-Policy 헤더 추가 또는 커스텀 Origin 정책


범주별 이슈 집계

분류 HIGH MED LOW
bug 1 1 0
security 4 2 0
quality 1 3 3
perf 0 0 2
tổng 6 8 5