Files
MBN_STOCK_WEBVIEW/docs/LEGACY_ONE_TIME_IMPORT_AUDIT.md

7.1 KiB

원본 one-time importer 안전 감사

기준일은 2026-07-12이며, 설치된 패키지는 Wickedness.MBNStockWebView_1.0.3.0_x64__qbv3jkvsn3aj0이다. 이 감사에서는 원본 Data를 읽기 전용으로 사용했고 Oracle/MariaDB에는 SELECT만 실행했다. 운영 %LOCALAPPDATA%\MBN_STOCK_WEBVIEW\OperatorData와 Tornado2에는 쓰기나 명령을 보내지 않았다. 비교쌍에 한해서는 설치된 signed package의 명시적 버튼을 한 번 눌러 WebView2 localStorage에 8쌍과 one-time marker를 같은 envelope로 저장했다.

감사 시점 운영 profile 분류

FSell/VI 저장소에는 marker와 intent 없이 원본과 byte 단위로 같은 네 파일이 이미 있다.

파일 길이 SHA-256 고정 원본과 일치
개인.dat 56 F953366A5D9F940CBE54F05C256F8BFB0AF4674C8F4D46CCBF839A63113342FA
외국인.dat 58 45F62038E3216E80FCF22F4E923056858E9C3537BF0DC46BC359148E82DD7CD8
기관.dat 27 BC4D62D734E701744E10DD830A2D6B9AEBEBEB795BB4DABC77CDE6577F016EB7
VI발동.dat 276 46AFBA07D2C4AF50717450582DEB10EC10367701D09161BECF5E32342AD4AA45
  • .legacy-manual-operator-import.v1.json: 없음
  • .legacy-manual-operator-import.v1.in-progress.json: 없음
  • importer staging 파일: 없음
  • startup 상태: marker가 없는 기존 원본 파일을 그대로 쓰는 ReadyLegacyData
  • 현재 버튼 실행 판정: DestinationNotEmpty, OutcomeUnknown=false

따라서 현재 profile에서 원본 데이터 가져오기를 누르면 네 파일을 덮어쓰지 않고 source read 전에 전체 import가 중단된다. marker를 임의로 만들어 AlreadyImported로 바꾸거나 파일을 지워 import를 재현하면 안 된다.

다음 명령은 운영 profile을 쓰지 않고 이 판정을 다시 출력한다.

dotnet run --project .\tools\MBN_STOCK_WEBVIEW.DbSmoke\MBN_STOCK_WEBVIEW.DbSmoke.csproj `
  -c Release -p:Platform=x64 -- --manual-import-audit

현재 결과:

MANUAL_IMPORT_AUDIT: PASS sourceFiles=4/4 operatingFiles=4/4 sourceByteIdentical=true marker=false intent=false staged=false runtime=ReadyLegacyData decision=DestinationNotEmpty outcomeUnknown=false writesAttempted=false

격리 FSell/VI 실제 원본 import

--manual-import-fixture는 사용자가 destination을 지정할 수 없게 닫혀 있다. 고정 원본을 읽고 %TEMP%\MBN_STOCK_WEBVIEW-manual-import-fixture-<GUID>에만 쓴다. 전체 실행은 1분 timeout으로 제한되며 timeout/cancel 뒤 자동 재시도하지 않는다. 다음 항목을 모두 확인한 뒤 자신이 만든 prefix의 non-reparse directory만 삭제한다.

  1. 4파일 stage 및 no-overwrite commit
  2. production parser read-back
  3. marker-last commit과 intent 제거
  4. 재시작 guard의 ReadyImportedData
  5. 같은 fixture의 두 번째 실행 AlreadyImported, OutcomeUnknown=false
  6. 원본 4파일과 운영 import 대상 6항목의 실행 전후 SHA-256 불변
  7. 원본 aggregate SHA-256 fd39a363…a2114, FSell 15행과 VI 9건 고정
dotnet run --project .\tools\MBN_STOCK_WEBVIEW.DbSmoke\MBN_STOCK_WEBVIEW.DbSmoke.csproj `
  -c Release -p:Platform=x64 -- --manual-import-fixture

실제 결과:

MANUAL_IMPORT_FIXTURE: PASS rows=15 vi=9 sourceSha256=fd39a3639e574a19f174225e8e65234d6bdd4c58700bb5d10230a779a03a2114 state=ReadyImportedData secondAttempt=AlreadyImported outcomeUnknown=false sourceUnchanged=true operatingImportSetUnchanged=true

종목비교.dat 분류

  • 고정 원본: 358바이트, 8행
  • SHA-256: 1EE76BC464FB1C44C8B6546E99CDC21C726C0E2C24AD344F5C19AE41BFC0D2F2
  • 실제 Oracle/MariaDB read-only 재검증: 8/8 identity 해소
  • 실행 경로: 비교 화면의 원본 비교쌍 가져오기
  • 성공 저장 위치: package WebView2 localStorage의 단일 comparison envelope

감사 초기에 package WebView2가 닫힌 상태에서 LevelDB에는 87바이트 VERSION=1 log 외 comparison key byte가 없었고 비교 화면은 0 PAIRS였다. signed 1.0.3 패키지에서 원본 비교쌍 가져오기를 명시적으로 한 번 실행한 결과, native가 고정 원본의 SHA-256과 8행을 다시 읽고 현재 DB에서 모든 identity를 해소한 뒤 화면이 8 PAIRS원본 8쌍 가져옴으로 바뀌었다. 부분 저장이나 복구 필요 상태는 발생하지 않았다.

이 성공은 같은 package profile의 단일 comparison envelope에 pair list와 marker를 함께 기록한 결과다. 앱을 정상 종료한 뒤 설치된 signed 1.0.3을 다시 실행해 다음 상태를 확인했다.

  • 비교 화면: 8 PAIRS
  • 저장 비교쌍: 원본 순서의 8개 항목 유지
  • one-time gate: 완료형 원본 8쌍 가져옴, 다시 실행할 수 없는 상태
  • 고정 원본 SHA-256: 1EE76BC464FB1C44C8B6546E99CDC21C726C0E2C24AD344F5C19AE41BFC0D2F2로 불변

따라서 package UI 저장과 재시작 marker 영속성은 PASS다. 실행 중 LevelDB는 process lock 상태이므로 raw 파일 검색으로 live logical state를 단정하거나 파일을 복사·수정하지 않았다.

정규 DB smoke에는 실제 importer를 추가했다. 아래 명령은 원본을 읽고 8개 identity를 fresh SELECT로 해소하지만 package localStorage에는 쓰지 않는다.

dotnet run --project .\tools\MBN_STOCK_WEBVIEW.DbSmoke\MBN_STOCK_WEBVIEW.DbSmoke.csproj `
  -c Release -p:Platform=x64 --no-build

확인 결과는 다음과 같다.

LegacyComparisonImport: rows=8, sha256=1EE76BC464FB1C44C8B6546E99CDC21C726C0E2C24AD344F5C19AE41BFC0D2F2, identities=fresh-read-only
REAL_DB_SMOKE: PASS

현재 package profile에서는 실제 0→8 import와 정상 종료 후 재시작 marker 검증을 완료했으므로 이를 재현하려고 package family 데이터를 초기화하지 않는다. 빈 profile의 실패 fixture가 추가로 필요하면 별도 test package identity를 사용한다. 운영 identity에서 Remove-AppxPackage, app data reset, LevelDB 파일 복사·삭제로 빈 profile을 만들지 않는다.

차단 판정표

상태 FSell/VI 판정 비교쌍 판정 자동 재시도
대상 파일 없음, source 정상 import 가능 marker 없고 envelope 정상일 때 가능 명시적 버튼만
기존 4파일, marker 없음 DestinationNotEmpty 해당 없음 금지
유효 marker 있음 AlreadyImported 버튼이 원본 N쌍 가져옴으로 비활성 금지
intent 있음 InterruptedImport, OutcomeUnknown=true 해당 없음 금지, 수동 대조
marker/4파일 불일치 InvalidImportedState 해당 없음 금지, 수동 대조
split/손상 envelope 해당 없음 복구 필요, 전체 차단 금지, local state 대조
DB timeout/identity 오류 해당 없음 저장 전 전체 취소 금지

FSell/VI 전용 화면은 원본 운영 항목의 개인·외국인·기관 또는 VI 수동 항목을 열어 진입하며, header의 원본 데이터 가져오기가 importer 버튼이다. 비교쌍은 상단 비교 업무 탭 안의 원본 비교쌍 가져오기 버튼으로 진입한다.