Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
review_type: full
language: ko
file_patterns: "src/**/*.js,test-files/**/*.js,*.yml,*.json,*.md"
file_patterns: "**/*.js,**/*.ts,**/*.jsx,**/*.tsx,**/*.py,**/*.java,**/*.go,**/*.rs"
exclude_patterns: "dist/**,node_modules/**,test/**"
max_files: 8
severity_filter: medium
Expand Down
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 chimaek

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Claude API를 활용한 지능형 AI 코드 리뷰 GitHub Action입니다. Pull
... (상세한 이슈 목록)
```

![PR Review Comment](https://github.com/chimaek/claude-code-review-action/blob/master/example/images/pr_example.png)
![PR Review Comment](https://github.com/chimaek/claude-code-review-action/blob/master/examples/images/pr_example.png)

## 🚀 빠른 시작

Expand Down
194 changes: 194 additions & 0 deletions examples/DatabaseManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// Java 예시: 데이터베이스 매니저 (보안 및 성능 이슈)
package com.example.database;

import java.sql.*;
import java.util.*;
import java.io.FileWriter;
import java.io.IOException;
import java.util.logging.Logger;

public class DatabaseManager {
// 보안 이슈: 하드코딩된 데이터베이스 정보
private static final String DB_URL = "jdbc:mysql://localhost:3306/mydb";
private static final String USERNAME = "admin";
private static final String PASSWORD = "password123";

private Connection connection;
private static Logger logger = Logger.getLogger(DatabaseManager.class.getName());

// 성능 이슈: 싱글톤 패턴 미사용, 매번 새 연결
public DatabaseManager() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
connection = DriverManager.getConnection(DB_URL, USERNAME, PASSWORD);
} catch (Exception e) {
// 보안 이슈: 상세한 에러 정보 노출
logger.severe("Database connection failed: " + e.getMessage());
e.printStackTrace();
}
}

// 보안 이슈: SQL Injection 취약점
public List<User> getUsersByRole(String role) {
List<User> users = new ArrayList<>();

try {
// 파라미터 바인딩 없이 문자열 연결
String query = "SELECT * FROM users WHERE role = '" + role + "'";
logger.info("Executing query: " + query); // 쿼리 로깅 (정보 노출)

Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);

while (rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password")); // 보안 이슈: 비밀번호 포함
user.setRole(rs.getString("role"));
users.add(user);
}

// 리소스 해제 안함 (메모리 누수)

} catch (SQLException e) {
logger.severe("Query execution failed: " + e.getMessage());
}

return users;
}

// 성능 이슈: N+1 쿼리 문제
public void updateUserProfiles(List<Integer> userIds) {
for (Integer userId : userIds) {
try {
// 각 사용자마다 개별 쿼리 실행
String query = "UPDATE users SET last_updated = NOW() WHERE id = ?";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setInt(1, userId);
stmt.executeUpdate();

// 연결 닫지 않음 (리소스 누수)

} catch (SQLException e) {
logger.severe("Update failed for user " + userId + ": " + e.getMessage());
}
}
}

// 보안 이슈: 권한 검증 없는 삭제
public boolean deleteUser(String username) {
try {
// 권한 확인 없이 삭제 실행
String query = "DELETE FROM users WHERE username = '" + username + "'";
Statement stmt = connection.createStatement();
int affectedRows = stmt.executeUpdate(query);

// 보안 이슈: 삭제된 사용자 정보 로깅
logger.info("Deleted user: " + username + ", affected rows: " + affectedRows);

return affectedRows > 0;
} catch (SQLException e) {
logger.severe("User deletion failed: " + e.getMessage());
return false;
}
}

// 성능 이슈: 대용량 데이터를 한번에 메모리로 로드
public List<String> getAllUserEmails() {
List<String> emails = new ArrayList<>();

try {
String query = "SELECT email FROM users"; // LIMIT 없음
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(query);

// 모든 결과를 메모리에 저장
while (rs.next()) {
emails.add(rs.getString("email"));
}

} catch (SQLException e) {
logger.severe("Failed to fetch emails: " + e.getMessage());
}

return emails;
}

// 보안 이슈: 민감한 정보를 평문 파일로 저장
public void exportUserData(List<User> users) {
try {
FileWriter writer = new FileWriter("user_export.txt");

for (User user : users) {
// 비밀번호 포함하여 내보내기
String line = user.getId() + "," + user.getUsername() + "," +
user.getPassword() + "," + user.getRole() + "\n";
writer.write(line);
}

writer.close();
logger.info("User data exported to user_export.txt");

} catch (IOException e) {
logger.severe("Export failed: " + e.getMessage());
}
}

// 코드 스타일 이슈: 네이밍 컨벤션 위반
public void ExecuteBatchOperation(List<String> SQLQueries) {
try {
connection.setAutoCommit(false);
Statement stmt = connection.createStatement();

// 코드 스타일 이슈: 일관성 없는 들여쓰기
for(String query : SQLQueries) {
stmt.addBatch(query); // SQL Injection 가능
logger.info("Added to batch: " + query);
}

int[] results = stmt.executeBatch();
connection.commit();

// 성능 이슈: 불필요한 로깅
for (int i = 0; i < results.length; i++) {
logger.info("Batch operation " + i + " affected " + results[i] + " rows");
}

} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException rollbackError) {
logger.severe("Rollback failed: " + rollbackError.getMessage());
}
logger.severe("Batch operation failed: " + e.getMessage());
}
}

// 메모리 누수: finalize에서 리소스 정리 (잘못된 패턴)
@Override
protected void finalize() throws Throwable {
if (connection != null && !connection.isClosed()) {
connection.close();
}
super.finalize();
}
}

// 코드 스타일 이슈: 같은 파일에 여러 클래스
class User {
private int id;
private String username;
private String password;
private String role;

// getter/setter 생략
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; }
}
123 changes: 123 additions & 0 deletions examples/data_analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Python 예시: 데이터 분석기 (성능 및 보안 이슈)
import os
import pickle
import hashlib
import subprocess
import pandas as pd
from typing import List, Dict, Any

class DataAnalyzer:
def __init__(self, data_path: str):
self.data_path = data_path
self.cache = {}
self.api_key = "sk-1234567890abcdef" # 보안 이슈: 하드코딩된 API 키

# 보안 이슈: 경로 순회 공격 가능
def load_data(self, filename: str) -> pd.DataFrame:
# 입력 검증 없음
full_path = os.path.join(self.data_path, filename)
print(f"Loading data from: {full_path}") # 경로 정보 노출

try:
# 보안 이슈: pickle.load 사용 (코드 실행 가능)
if filename.endswith('.pkl'):
with open(full_path, 'rb') as f:
return pickle.load(f) # 매우 위험!
else:
return pd.read_csv(full_path)
except Exception as e:
print(f"Error loading data: {str(e)}") # 에러 정보 노출
return pd.DataFrame()

# 성능 이슈: 비효율적인 데이터 처리
def analyze_data(self, data: pd.DataFrame) -> Dict[str, Any]:
results = {}

# 성능 이슈: 반복문으로 통계 계산
for column in data.columns:
column_stats = []
for value in data[column]:
# 매우 비효율적인 처리
if str(value).isdigit():
column_stats.append(float(value))

if column_stats:
results[column] = {
'mean': sum(column_stats) / len(column_stats),
'max': max(column_stats),
'min': min(column_stats)
}

return results

# 보안 이슈: 명령어 인젝션 가능
def execute_query(self, query: str) -> str:
# 입력 검증 없이 시스템 명령어 실행
command = f"mysql -e '{query}'"
result = subprocess.run(command, shell=True, capture_output=True, text=True)
return result.stdout

# 메모리 누수: 캐시 정리 없음
def cache_result(self, key: str, data: Any) -> None:
# 캐시 크기 제한 없음
self.cache[key] = data
print(f"Cached {len(str(data))} bytes for key: {key}")

# 보안 이슈: 약한 해시 함수 사용
def generate_hash(self, data: str) -> str:
# MD5는 보안상 취약함
return hashlib.md5(data.encode()).hexdigest()

# 성능 이슈: 중첩 반복문
def find_duplicates(self, data_list: List[Dict]) -> List[Dict]:
duplicates = []

# O(n²) 복잡도
for i in range(len(data_list)):
for j in range(i + 1, len(data_list)):
if data_list[i] == data_list[j]:
duplicates.append(data_list[i])

return duplicates

# 코드 스타일 이슈: 네이밍 컨벤션 위반
def ProcessLargeDataset(self, DataSet: List) -> None:
# 변수명이 일관성 없음
processedData = []
Total_Count = 0

for item in DataSet:
# 들여쓰기 일관성 없음
processed_item = self.process_item(item)
processedData.append(processed_item)
Total_Count += 1

print(f"Processed {Total_Count} items")

def process_item(self, item):
# 타입 힌트 없음
return item

# 보안 이슈: 민감한 정보 로깅
def authenticate_user(self, username: str, password: str) -> bool:
print(f"Authenticating user: {username} with password: {password}") # 비밀번호 로깅!

# 보안 이슈: 약한 비밀번호 정책
if len(password) >= 4:
return True
return False

# 메모리 누수: 대용량 데이터 처리 시 문제
def load_all_data(self) -> List[pd.DataFrame]:
all_data = []

# 메모리 사용량 고려 없음
for filename in os.listdir(self.data_path):
if filename.endswith(('.csv', '.pkl')):
data = self.load_data(filename)
all_data.append(data) # 모든 데이터를 메모리에 보관

return all_data

# 모듈 레벨에서 인스턴스 생성 (좋지 않은 패턴)
analyzer = DataAnalyzer("/tmp/data")
Loading