WIP 86 보충: 7z · RAR 의 한글 비밀번호 풀기 - 7-Zip 표준 입력을 UTF-8 로 (검토 대기)

관리자 지시 (10/18): -sccUTF-8 로 표준 입력을 UTF-8 로 보내 보고, 되면 그것을 쓴다.
- 10/18 실제 7z.exe 시험: 7z 형식 (머리까지 암호) 의 한글 비밀번호는 -sccUTF-8 + UTF-8 표준 입력이면 풀림 (exit 0).
  앱의 _run7z 는 -sccUTF-8 을 붙이면서 표준 입력은 Dart 기본 (시스템 코드 페이지 = CP949) 으로 보내 실패하고 있었다 (CP949 + -sccUTF-8 = exit 2 확인).
  → pr.stdin.encoding = utf8. 7z · RAR 의 목록 · 풀기 · 비밀번호 모두 같은 길 (RAR 은 만들 수 없어 실제 RAR 로는 시험 못 함 - 같은 비밀번호 읽기)
- ZIP 만들기는 그대로: 7-Zip 의 ZIP 은 영문 밖 비밀번호를 (표준 입력 · 명령 줄 모두) 받지 않아 archive AES + 512MB 제한 + 경고 (관리자 승인).
  ZIP 풀기는 archive (앞 커밋의 UTF-8 바이트 고침) - 명령 줄 -p 는 쓰지 않음 (프로세스 목록에 보임, 관리자 지시)

시험: archive_ops 14 통과 (새: 한글 비밀번호 7z - 틀린 비밀번호 · 목록 · 풀기. 시험 압축만 명령 줄 -p 로 만듦).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zip_aes
RyuJongJin 2026-10-18 00:13:09 +09:00
parent 16f33b3b71
commit b0ac3c4581
2 changed files with 17 additions and 0 deletions

View File

@ -451,6 +451,9 @@ List<ArchiveEntry> parse7zList(String out) {
Future<(int, String, String)> _run7z(String exe, List<String> args,
{String? password, void Function(String line)? onLine, bool Function()? cancelled, String? workingDirectory, bool confirm = false}) async {
final pr = await Process.start(exe, ['-sccUTF-8', '-scsUTF-8', ...args], workingDirectory: workingDirectory);
// -sccUTF-8 이면 7-Zip 은 표준 입력도 UTF-8 로 읽는다 - Dart 의 기본 (시스템 코드 페이지, 한국어 Windows 는 CP949) 으로 보내면
// 한글 비밀번호가 틀렸다고 나왔다 (관리자 지시로 10/18 시험: UTF-8 + -sccUTF-8 이면 7z · RAR 이 한글 비밀번호로 풀림)
pr.stdin.encoding = utf8;
// 묻지 않을 때도 빈 줄을 보내 기다리지 않게 (암호가 걸렸는데 비밀번호가 없으면 틀림으로 끝난다)
pr.stdin.writeln(password ?? '');
// 만들 때는 "다시 넣기" 도 묻는다 (zip_aes)

View File

@ -238,6 +238,20 @@ void main() {
}, skip: has ? false : '7-Zip 이 없음 (tool/fetch_7zip.ps1)');
}
test('zip_aes: 한글 비밀번호 7z 풀기 - 표준 입력을 UTF-8 로 (머리까지 암호 · 목록 · 풀기 · 틀린 비밀번호)', () async {
File(p.join(tmp.path, 'a.txt')).writeAsStringSync('안');
final arc = p.join(tmp.path, 'k.7z');
// 시험 압축만 명령 줄 -p 로 만든다 (앱은 비밀번호를 명령 줄에 넣지 않는다)
final mk = await Process.run(exe, ['a', '-t7z', '-mhe=on', '-p비번 123', arc, p.join(tmp.path, 'a.txt')]);
expect(mk.exitCode, 0, reason: '${mk.stdout}');
await expectLater(list7z(exe, arc, password: '틀림'), throwsA(isA<ArchivePasswordException>()));
final list = await list7z(exe, arc, password: '비번 123');
expect(list.map((e) => e.name), ['a.txt']);
final out = Directory(p.join(tmp.path, '풀림'))..createSync();
await extract7zPlan(exe, arc, out.path, planExtract(list), password: '비번 123');
expect(File(p.join(out.path, 'a.txt')).readAsStringSync(), '안');
}, skip: has ? false : '7-Zip 이 없음 (tool/fetch_7zip.ps1)');
test('zip_aes: 취소 · 실패면 만들다 만 ZIP 을 남기지 않고, 빈 비밀번호는 받지 않음', () async {
final f = File(p.join(tmp.path, 'a.txt'))..writeAsStringSync('A');
final zip = p.join(tmp.path, 'c.zip');