Skip to content

Conversation

@ohah
Copy link
Owner

@ohah ohah commented Dec 13, 2025

  • Node.js 바이너리를 리소스에 포함하지 않고 앱 시작 시 캐시 경로에 자동 다운로드
  • node_downloader 모듈 추가하여 Node.js 바이너리 다운로드 및 관리
  • postinstall 스크립트 및 download-node-binaries.js 제거
  • tauri.conf.json에서 Node.js 바이너리 리소스 설정 제거
  • 앱 번들 크기 감소 및 버전 업데이트 용이

- Node.js 바이너리를 리소스에 포함하지 않고 앱 시작 시 캐시 경로에 자동 다운로드
- node_downloader 모듈 추가하여 Node.js 바이너리 다운로드 및 관리
- postinstall 스크립트 및 download-node-binaries.js 제거
- tauri.conf.json에서 Node.js 바이너리 리소스 설정 제거
- 앱 번들 크기 감소 및 버전 업데이트 용이
@ohah ohah self-assigned this Dec 13, 2025
@ohah ohah added the enhancement New feature or request label Dec 13, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the Node.js binary management strategy from bundling binaries as Tauri resources to downloading them on-demand at application startup and caching them in the system cache directory. This reduces the application bundle size and simplifies version updates by eliminating the need for platform-specific binaries in the repository.

Key Changes:

  • Introduces node_downloader module for automatic Node.js binary download and extraction
  • Removes npm postinstall script that previously downloaded binaries to resources directory
  • Updates binary resolution to prioritize cache directory over bundled resources

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
scripts/download-node-binaries.js Removes the Node.js postinstall download script (no longer needed)
package.json Removes download-node script and postinstall hook
crates/node-runtime/src/node_downloader.rs Adds new module for downloading, extracting, and caching Node.js binaries
crates/node-runtime/src/lib.rs Updates binary resolution to check cache directory first, integrates with node_downloader
crates/node-runtime/Cargo.toml Adds dependencies for HTTP requests and archive extraction (reqwest, tar, xz2, zip, dirs)
apps/executeJS/src-tauri/tauri.conf.json Removes Node.js binary resources from bundle configuration
apps/executeJS/src-tauri/src/lib.rs Adds Node.js binary initialization during app startup
Cargo.lock Updates dependency tree with new compression and HTTP libraries

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 67 to 134
let node_dir = cache_dir.join(format!("node-v24.12.0-{}-{}", os_name, arch));
let node_path = node_dir.join(&binary_name);

// 이미 존재하면 반환
if node_path.exists() {
tracing::info!("Node.js 바이너리 발견: {}", node_path.display());
Self::set_permissions_if_needed(&node_path)?;
return Ok(node_path);
}

// 다운로드 필요
tracing::info!(
"Node.js 바이너리 다운로드 시작... (경로: {})",
node_path.display()
);
if let Err(e) = Self::download_node_binary().await {
tracing::error!("Node.js 바이너리 다운로드 실패: {}", e);
return Err(e);
}

// 다운로드 후 다시 확인
if node_path.exists() {
Self::set_permissions_if_needed(&node_path)?;
Ok(node_path)
} else {
anyhow::bail!(
"Node.js 바이너리 다운로드 후에도 파일을 찾을 수 없습니다: {}",
node_path.display()
);
}
}

/// Node.js 바이너리 다운로드
async fn download_node_binary() -> Result<()> {
let (os_name, arch, extension, binary_name) = Self::get_platform_info()?;
let cache_dir = Self::cache_dir()?;
// find_node_binary와 동일한 경로 형식 사용
let node_dir = cache_dir.join(format!("node-v24.12.0-{}-{}", os_name, arch));

let file_name = format!("node-{}-{}-{}.{}", NODE_VERSION, os_name, arch, extension);
let download_url = format!("{}{}", BASE_URL, file_name);

tracing::info!("Node.js 다운로드 시작: {}", download_url);
tracing::info!("캐시 디렉토리: {}", cache_dir.display());
tracing::info!("타겟 디렉토리: {}", node_dir.display());

// 다운로드
let response = reqwest::get(&download_url)
.await
.context("Node.js 다운로드 실패")?;

let bytes = response
.bytes()
.await
.context("다운로드 데이터 읽기 실패")?;

// 임시 파일에 저장
let temp_file = cache_dir.join(&file_name);
let mut file = fs::File::create(&temp_file).context("임시 파일 생성 실패")?;
file.write_all(&bytes).context("파일 쓰기 실패")?;
drop(file); // 파일 핸들 닫기

// 압축 해제
tracing::info!("압축 해제 중...");
fs::create_dir_all(&cache_dir).context("캐시 디렉토리 생성 실패")?;

// 압축 해제 후 디렉토리 이름 (Node.js 배포본의 실제 디렉토리 이름)
let extracted_dir_name = format!("node-{}-{}-{}", NODE_VERSION, os_name, arch);
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded version string "v24.12.0" is duplicated in multiple locations (lines 67, 104, 134) within this module and also appears in lib.rs. This creates a maintenance burden and increases the risk of version mismatch errors. Consider using the NODE_VERSION constant consistently throughout, or better yet, derive these paths from NODE_VERSION to ensure consistency.

Copilot uses AI. Check for mistakes.
Comment on lines 114 to 127
let response = reqwest::get(&download_url)
.await
.context("Node.js 다운로드 실패")?;

let bytes = response
.bytes()
.await
.context("다운로드 데이터 읽기 실패")?;

// 임시 파일에 저장
let temp_file = cache_dir.join(&file_name);
let mut file = fs::File::create(&temp_file).context("임시 파일 생성 실패")?;
file.write_all(&bytes).context("파일 쓰기 실패")?;
drop(file); // 파일 핸들 닫기
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The download process does not verify file integrity after downloading. Node.js provides SHA256 checksums for all release files. Without checksum verification, the application could execute a corrupted or tampered binary, posing a security risk. Consider downloading and verifying the SHASUMS256.txt file from the release directory to ensure the downloaded binary is authentic and uncorrupted.

Copilot uses AI. Check for mistakes.
Comment on lines 258 to 270
// 에러 메시지
let cache_path = cache_dir.join(format!("node-v24.12.0-{}-{}", os_name, arch));
anyhow::bail!(
"Node.js 바이너리를 찾을 수 없습니다.\n\
- 개발 모드 경로: {}\n\
- 실행 파일 경로: {}\n\
- 캐시 경로: {}\n\
- OS: {}, Arch: {}\n\
- 바이너리 이름: {}\n\
src-tauri/resources/node-runtime/ 폴더에 Node.js 바이너리가 있는지 확인하세요.",
error_path.display(),
exe_info,
앱을 재시작하면 자동으로 다운로드됩니다.",
cache_path.display(),
std::env::consts::OS,
std::env::consts::ARCH,
binary_name
);
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message instructs users to "restart the app to automatically download" the Node.js binary, but this message is shown when find_node_binary() fails. However, the automatic download only happens via the ensure_node_binary() call during app startup, which is separate from find_node_binary(). If a user creates a NodeExecutor directly without having called ensure_node_binary first, restarting won't help because NodeExecutor::new() calls find_node_binary() which doesn't trigger downloads. This could mislead users about how to fix the issue.

Copilot uses AI. Check for mistakes.
Comment on lines 1 to 226
use anyhow::{Context, Result};
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use tar::Archive;
use xz2::read::XzDecoder;
use zip::ZipArchive;

const NODE_VERSION: &str = "v24.12.0";
const BASE_URL: &str = "https://nodejs.org/dist/v24.12.0/";

pub struct NodeDownloader;

impl NodeDownloader {
/// 캐시 디렉토리 경로 반환
pub fn cache_dir() -> Result<PathBuf> {
let cache_dir = dirs::cache_dir()
.context("캐시 디렉토리를 찾을 수 없습니다")?
.join("executejs")
.join("node-runtime");

fs::create_dir_all(&cache_dir).context("캐시 디렉토리를 생성할 수 없습니다")?;

Ok(cache_dir)
}

/// OS 및 아키텍처 정보 반환
pub fn get_platform_info() -> Result<(String, String, String, String)> {
let (os_name, arch, extension, binary_name) = if cfg!(target_os = "windows") {
let arch = if cfg!(target_arch = "aarch64") {
"arm64"
} else {
"x64"
};
("win", arch, "zip", "node.exe")
} else if cfg!(target_os = "macos") {
let arch = if cfg!(target_arch = "aarch64") {
"arm64"
} else {
"x64"
};
("darwin", arch, "tar.xz", "node")
} else if cfg!(target_os = "linux") {
let arch = if cfg!(target_arch = "aarch64") {
"arm64"
} else {
"x64"
};
("linux", arch, "tar.xz", "node")
} else {
anyhow::bail!("지원하지 않는 운영체제입니다: {}", std::env::consts::OS);
};

Ok((
os_name.to_string(),
arch.to_string(),
extension.to_string(),
binary_name.to_string(),
))
}

/// Node.js 바이너리 경로 반환 (다운로드 필요 시 다운로드)
pub async fn ensure_node_binary() -> Result<PathBuf> {
let (os_name, arch, _extension, binary_name) = Self::get_platform_info()?;
let cache_dir = Self::cache_dir()?;
// find_node_binary와 동일한 경로 형식 사용
let node_dir = cache_dir.join(format!("node-v24.12.0-{}-{}", os_name, arch));
let node_path = node_dir.join(&binary_name);

// 이미 존재하면 반환
if node_path.exists() {
tracing::info!("Node.js 바이너리 발견: {}", node_path.display());
Self::set_permissions_if_needed(&node_path)?;
return Ok(node_path);
}

// 다운로드 필요
tracing::info!(
"Node.js 바이너리 다운로드 시작... (경로: {})",
node_path.display()
);
if let Err(e) = Self::download_node_binary().await {
tracing::error!("Node.js 바이너리 다운로드 실패: {}", e);
return Err(e);
}

// 다운로드 후 다시 확인
if node_path.exists() {
Self::set_permissions_if_needed(&node_path)?;
Ok(node_path)
} else {
anyhow::bail!(
"Node.js 바이너리 다운로드 후에도 파일을 찾을 수 없습니다: {}",
node_path.display()
);
}
}

/// Node.js 바이너리 다운로드
async fn download_node_binary() -> Result<()> {
let (os_name, arch, extension, binary_name) = Self::get_platform_info()?;
let cache_dir = Self::cache_dir()?;
// find_node_binary와 동일한 경로 형식 사용
let node_dir = cache_dir.join(format!("node-v24.12.0-{}-{}", os_name, arch));

let file_name = format!("node-{}-{}-{}.{}", NODE_VERSION, os_name, arch, extension);
let download_url = format!("{}{}", BASE_URL, file_name);

tracing::info!("Node.js 다운로드 시작: {}", download_url);
tracing::info!("캐시 디렉토리: {}", cache_dir.display());
tracing::info!("타겟 디렉토리: {}", node_dir.display());

// 다운로드
let response = reqwest::get(&download_url)
.await
.context("Node.js 다운로드 실패")?;

let bytes = response
.bytes()
.await
.context("다운로드 데이터 읽기 실패")?;

// 임시 파일에 저장
let temp_file = cache_dir.join(&file_name);
let mut file = fs::File::create(&temp_file).context("임시 파일 생성 실패")?;
file.write_all(&bytes).context("파일 쓰기 실패")?;
drop(file); // 파일 핸들 닫기

// 압축 해제
tracing::info!("압축 해제 중...");
fs::create_dir_all(&cache_dir).context("캐시 디렉토리 생성 실패")?;

// 압축 해제 후 디렉토리 이름 (Node.js 배포본의 실제 디렉토리 이름)
let extracted_dir_name = format!("node-{}-{}-{}", NODE_VERSION, os_name, arch);
let extracted_dir = cache_dir.join(&extracted_dir_name);

if extension == "tar.xz" {
// tar.xz 압축 해제
let tar_xz = fs::File::open(&temp_file)?;
let tar = XzDecoder::new(tar_xz);
let mut archive = Archive::new(tar);
archive
.unpack(&cache_dir)
.context("tar.xz 압축 해제 실패")?;
} else if extension == "zip" {
// Windows: zip 압축 해제
let zip_file = fs::File::open(&temp_file)?;
let mut archive = ZipArchive::new(zip_file)?;
archive.extract(&cache_dir).context("zip 압축 해제 실패")?;
} else {
anyhow::bail!("지원하지 않는 압축 형식: {}", extension);
}

// 바이너리 찾기 및 이동
let source_binary = if os_name == "win" {
extracted_dir.join(&binary_name)
} else {
extracted_dir.join("bin").join(&binary_name)
};

if !source_binary.exists() {
anyhow::bail!(
"압축 해제 후 바이너리를 찾을 수 없습니다: {}",
source_binary.display()
);
}

// 타겟 디렉토리 생성
fs::create_dir_all(&node_dir).context("Node.js 디렉토리 생성 실패")?;

// 바이너리 복사
let target_binary = node_dir.join(&binary_name);
tracing::info!("소스 바이너리: {}", source_binary.display());
tracing::info!("타겟 바이너리: {}", target_binary.display());

if source_binary != target_binary {
tracing::info!("바이너리 복사 중...");
fs::copy(&source_binary, &target_binary).context("바이너리 복사 실패")?;
tracing::info!("바이너리 복사 완료");
} else {
tracing::info!("바이너리가 이미 올바른 위치에 있습니다");
}

// 복사 후 확인
if !target_binary.exists() {
anyhow::bail!(
"바이너리 복사 후에도 파일을 찾을 수 없습니다: {}",
target_binary.display()
);
}

// 임시 파일 정리
tracing::info!("임시 파일 정리 중...");
let _ = fs::remove_file(&temp_file);

// extracted_dir와 node_dir가 같은 경우 삭제하지 않음 (바이너리가 이미 올바른 위치에 있음)
if extracted_dir != node_dir && extracted_dir.exists() {
tracing::info!("압축 해제 디렉토리 정리 중: {}", extracted_dir.display());
let _ = fs::remove_dir_all(&extracted_dir);
} else {
tracing::info!("압축 해제 디렉토리가 타겟 디렉토리와 동일하므로 정리하지 않음");
}

tracing::info!(
"Node.js 바이너리 다운로드 완료: {}",
target_binary.display()
);
Ok(())
}

/// 실행 권한 설정
fn set_permissions_if_needed(node_path: &Path) -> Result<()> {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let metadata = fs::metadata(node_path)?;
let perms = metadata.permissions();
if perms.mode() & 0o111 == 0 {
let mut new_perms = perms.clone();
new_perms.set_mode(0o755);
fs::set_permissions(node_path, new_perms)?;
}
}
Ok(())
}
}
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no test coverage for the new NodeDownloader module functionality. The existing tests only cover JavaScript execution, but don't test the download logic, platform detection, path construction, file extraction, or error handling in the download process. Consider adding unit tests for get_platform_info(), cache_dir(), and integration tests that verify the download and extraction process (potentially using a mock HTTP server).

Copilot uses AI. Check for mistakes.
ohah added 8 commits December 13, 2025 14:54
- 하드코딩된 버전 문자열을 NODE_VERSION 상수로 통일
- HTTP 응답 상태 코드 확인 추가
- 임시 파일 정리 시 에러 로깅 추가
- reqwest 의존성에 default-features = false 추가
- 사용하지 않는 flate2 의존성 제거
- 에러 메시지 개선 (find_node_binary vs ensure_node_binary)
- SHA256 체크섬 검증 추가 (파일 무결성 보장)
- 동시 다운로드 보호 (파일 락) 구현
- NodeDownloader 모듈 테스트 커버리지 추가
  - get_platform_info 테스트
  - cache_dir 테스트
  - NODE_VERSION 상수 테스트
  - base_url 테스트
- 캐시 경로만 사용하도록 단순화
- 개발 모드 src-tauri/resources/ 경로 찾기 제거
- 프로덕션 모드 실행 파일 기준 리소스 찾기 제거
- 모든 Node.js 바이너리는 캐시 경로에서만 로드
- execution.rs: ExecutionOutput 구조체 분리
- executor.rs: NodeExecutor 구조체 및 실행 로직 분리
- tests/executor_test.rs: 통합 테스트 코드 분리
- lib.rs: 모듈 선언 및 공개 API만 유지
- tests/node_downloader_test.rs: NodeDownloader 테스트 코드 분리
- node_downloader 모듈을 pub으로 공개하여 테스트에서 접근 가능하도록 변경
- src/tests/executor_test.rs 삭제 (올바른 위치는 tests/executor_test.rs)
- 모든 테스트 코드를 tests/ 디렉토리로 분리
- 소스 파일은 비즈니스 로직만 포함하도록 정리
- 불필요한 빈 줄 제거
Copilot AI review requested due to automatic review settings December 13, 2025 06:07
@ohah ohah merged commit c8c02b3 into main Dec 13, 2025
1 check passed
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +147 to +160
let bytes = response
.bytes()
.await
.context("다운로드 데이터 읽기 실패")?;

// SHA256 체크섬 검증
tracing::info!("파일 무결성 검증 중...");
Self::verify_checksum(&file_name, &bytes).await?;
tracing::info!("파일 무결성 검증 완료");

// 임시 파일에 저장
let temp_file = cache_dir.join(&file_name);
let mut file = fs::File::create(&temp_file).context("임시 파일 생성 실패")?;
file.write_all(&bytes).context("파일 쓰기 실패")?;
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The entire download file is loaded into memory (line 147-150) before writing to disk. For large Node.js binaries (which can be 30-50MB), this is inefficient and could cause memory issues, especially on resource-constrained systems. Consider using streaming writes instead, where the response is piped directly to the file as chunks are received, similar to how the deleted JavaScript version handled it with response.pipe(file).

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,2 @@
* @ohah @Enjoywater @Bori-github @ming-Jo

Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CODEOWNERS file ends with an unnecessary blank line. While this is a minor style issue, it's cleaner to remove trailing blank lines in configuration files.

Suggested change

Copilot uses AI. Check for mistakes.
let node_path = node_dir.join(&binary_name);
if node_path.exists() {
tracing::info!("다른 프로세스가 이미 다운로드를 완료했습니다");
// 락 파일 정리
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lock file is removed while still holding the lock guard, which could cause issues. The lock guard should be dropped before removing the lock file to ensure proper cleanup. Additionally, the lock file removal here occurs while the function still holds the file lock via the _lock_guard variable, which could lead to inconsistent state if the process crashes between releasing the lock and removing the file.

Suggested change
// 락 파일 정리
// 락 파일 정리 전에 락을 해제합니다.
drop(_lock_guard);

Copilot uses AI. Check for mistakes.
Comment on lines +311 to +317
if !response.status().is_success() {
tracing::warn!(
"체크섬 파일 다운로드 실패: HTTP {} - 무결성 검증을 건너뜁니다",
response.status()
);
return Ok(()); // 체크섬 검증 실패 시 경고만 하고 계속 진행
}
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checksum verification silently continues on failure (line 316) when the checksum file cannot be downloaded. This creates a security vulnerability where corrupted or malicious binaries could be installed if the checksum verification server is unavailable or returns an error. Consider making checksum verification mandatory for security-critical downloads, or at minimum, add a more prominent warning to users.

Copilot uses AI. Check for mistakes.
target_binary.display()
);

// 락 파일 정리
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lock file is removed at the end of the function while the lock guard is still in scope. This creates a potential race condition where the lock could be released but the file still exists, or vice versa. The lock guard should be explicitly dropped (using drop(_lock_guard)) before attempting to remove the lock file to ensure the file lock is released first.

Suggested change
// 락 파일 정리
// 락 가드 해제 후 락 파일 정리
drop(_lock_guard);

Copilot uses AI. Check for mistakes.
Comment on lines +8 to +14
impl ExecutionOutput {
pub fn new() -> Self {
Self {
stdout: String::new(),
stderr: String::new(),
}
}
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ExecutionOutput struct has a new() method but doesn't implement the Default trait. Since new() creates a default instance with empty strings, it would be more idiomatic to implement Default. This follows Rust best practices where types that have a canonical "empty" or "default" value should implement Default.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +65
use node_runtime::node_downloader::{NodeDownloader, NODE_VERSION};

#[test]
fn test_get_platform_info() {
let result = NodeDownloader::get_platform_info();
assert!(result.is_ok());

let (os_name, arch, extension, binary_name) = result.unwrap();
assert!(!os_name.is_empty());
assert!(!arch.is_empty());
assert!(!extension.is_empty());
assert!(!binary_name.is_empty());

// 플랫폼별 검증
#[cfg(target_os = "macos")]
{
assert_eq!(os_name, "darwin");
assert!(extension == "tar.xz");
assert_eq!(binary_name, "node");
}

#[cfg(target_os = "windows")]
{
assert_eq!(os_name, "win");
assert!(extension == "zip");
assert_eq!(binary_name, "node.exe");
}

#[cfg(target_os = "linux")]
{
assert_eq!(os_name, "linux");
assert!(extension == "tar.xz");
assert_eq!(binary_name, "node");
}
}

#[test]
fn test_cache_dir() {
let result = NodeDownloader::cache_dir();
assert!(result.is_ok());

let cache_dir = result.unwrap();
assert!(cache_dir.to_string_lossy().contains("executejs"));
assert!(cache_dir.to_string_lossy().contains("node-runtime"));
assert!(cache_dir.exists() || cache_dir.parent().unwrap().exists());
}

#[test]
fn test_node_version_constant() {
// NODE_VERSION 상수가 올바르게 정의되어 있는지 확인
assert_eq!(NODE_VERSION, "v24.12.0");
assert!(NODE_VERSION.starts_with('v'));
}

#[test]
fn test_base_url() {
// base_url은 private 함수이므로 직접 테스트할 수 없지만,
// NODE_VERSION을 통해 간접적으로 검증 가능
assert_eq!(NODE_VERSION, "v24.12.0");
let expected_url = format!("https://nodejs.org/dist/{}/", NODE_VERSION);
assert!(expected_url.contains("nodejs.org"));
assert!(expected_url.contains("dist"));
assert!(expected_url.contains(NODE_VERSION));
assert!(expected_url.ends_with('/'));
}
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test suite lacks coverage for the critical download and checksum verification functionality. The node_downloader_test.rs file only tests basic helper functions (get_platform_info, cache_dir, constants) but doesn't test ensure_node_binary, download_node_binary, verify_checksum, or acquire_download_lock. These are critical security and functionality components that should have test coverage, especially the checksum verification logic.

Copilot uses AI. Check for mistakes.
Comment on lines +348 to +352
tracing::warn!(
"체크섬 파일에서 {}에 대한 해시를 찾을 수 없습니다 - 무결성 검증을 건너뜁니다",
file_name
);
Ok(()) // 체크섬 파일에 없으면 경고만 하고 계속 진행
Copy link

Copilot AI Dec 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The checksum verification also silently continues (line 352) when the expected hash is not found in the SHASUMS256.txt file. This is a security concern as it allows installation of binaries without integrity verification. If the file name format changes or there's a mismatch, the binary will be installed without verification, which could allow compromised binaries to be used.

Suggested change
tracing::warn!(
"체크섬 파일에서 {}에 대한 해시를 찾을 수 없습니다 - 무결성 검증을 건너뜁니다",
file_name
);
Ok(()) // 체크섬 파일에 없으면 경고만 하고 계속 진행
anyhow::bail!(
"체크섬 파일에서 {}에 대한 해시를 찾을 수 없습니다 - 무결성 검증 실패",
file_name
);
// 체크섬 파일에 없으면 에러 반환

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants