당신의 ESLint 보안 플러그인이 80%의 취약점을 놓치고 있습니다 (증거가 있습니다)
I’m happy to translate the article for you, but I’ll need the full text you’d like translated (excluding the source line you already provided). Could you please paste the content you want translated into Korean? Once I have it, I’ll keep the source link at the top and preserve all formatting, markdown, and technical terms as requested.
벤치마크 방법론
테스트 파일
vulnerable.js (218줄) – 실제 취약점 12가지 카테고리 포함
// 1. Command Injection
exec(`ls -la ${userInput}`);
execSync('echo ' + userInput);
spawn('bash', ['-c', userInput]);
// 2. Path Traversal
fs.readFile(filename, 'utf8', callback);
fs.readFileSync(filename);
// 3. Object Injection
obj[key] = value;
data[key][value] = 'test';
// 4. SQL Injection
db.query('SELECT * FROM users WHERE id = ' + userId);
// 5. Code Execution
eval(code);
new Function(code);
// 6. Regex DoS
const evilRegex = /^(a+)+$/;
new RegExp(userInput);
// 7. Weak Cryptography
crypto.createHash('md5').update(password);
Math.random().toString(36);
// 8. Timing Attacks
if (inputToken === storedToken) {
return true;
}
// 9. XSS
document.getElementById('output').innerHTML = userContent;
// 10. Insecure Cookies
document.cookie = `${name}=${value}`;
// 11. Dynamic Require
require(moduleName);
// 12. Buffer Issues
const buf = new Buffer(size);
safe-patterns.js (167줄) – 경고를 NOT 트리거해야 하는 방어 패턴 포함
// Safe: Validated key access with allowlist
const VALID_KEYS = ['name', 'email', 'age'];
if (VALID_KEYS.includes(key)) {
return obj[key];
}
// Safe: hasOwnProperty check
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return obj[key];
}
// Safe: Path validation with startsWith
if (!safePath.startsWith(SAFE_DIR)) throw new Error('Invalid');
fs.readFileSync(safePath);
// Safe: Timing‑safe comparison
crypto.timingSafeEqual(bufA, bufB);
// Safe: DOMPurify sanitization
const clean = DOMPurify.sanitize(userContent);
element.innerHTML = clean;
벤치마크 구성
- Iterations: 테스트당 5회 실행
- Metrics: 평균 시간, 최소/최대 시간, 발견된 이슈, 트리거된 규칙
- Assumption: 실행 간 변동 ≤ 15 %; 보고된 차이 (2.83×, 3.8×)는 이 한계를 초과합니다
테스트 1 – 공정한 대결 (동일한 14개 규칙)
먼저, 두 플러그인을 양쪽 패키지에 존재하는 14개의 동등한 규칙만 사용하여 테스트했습니다. 이는 사과‑대‑사과 비교를 보장합니다.
결과
| 측정항목 | secure-coding | security | 승자 |
|---|---|---|---|
| 성능 / 이슈 | 24.95 ms | 25.12 ms | 🟢 secure-coding |
| 총 시간 | 723.54 ms | 527.58 ms | 🔵 security |
| 발견된 이슈 | 29 | 21 | 🟢 secure-coding |
| 탐지율 | 138 % | 100 % | 🟢 secure-coding |
규칙별 탐지
| 규칙 카테고리 | security | secure-coding | 차이 |
|---|---|---|---|
| 타이밍 공격 | 1 | 5 | +4 🟢 |
| 자식 프로세스 | 2 | 4 | +2 🟢 |
| 비리터럴 정규식 | 1 | 3 | +2 🟢 |
| Eval / 코드 실행 | 1 | 2 | +1 🟢 |
| 불충분한 난수성 | 0 | 1 | +1 🟢 |
| 파일 시스템 경로 탐색 | 5 | 5 | = |
| 객체 주입 | 5 | 5 | = |
| 동적 Require | 2 | 2 | = |
| 위험한 정규식 | 2 | 2 | = |
| Buffer API | 2 | 0 | -2 🔵 |
| 총계 | 21 | 29 | +8 |
핵심 발견: 동일한 규칙 카테고리에서 secure-coding은 38 % 더 많은 이슈를 찾아내면서도 이슈당 거의 동일한 효율성을 유지합니다.
테스트 2 – 권장 프리셋
다음으로, 각 플러그인의 권장 설정(기본 제공 경험)을 테스트했습니다.
결과
| 측정항목 | secure-coding | security | 승자 |
|---|---|---|---|
| 성능 / 이슈 | 9.95 ms | 28.16 ms | 🟢 secure-coding |
| 총 시간 | 795.99 ms | 591.41 ms | 🔵 security |
| 발견된 이슈 | 80 | 21 | 🟢 secure-coding |
| 트리거된 규칙 | 30 | 10 | 🟢 secure-coding |
| 전체 규칙 | 89 | 14 | 🟢 secure-coding |
탐지 세부 내역
secure-coding 규칙이 vulnerable.js에서 트리거됨:
• no-unvalidated-user-input: 8 이슈
• detect-non-literal-fs-filename: 5 이슈
• detect-object-injection: 5 이슈
• no-timing-attack: 5 이슈
• detect-child-process: 4 이슈
• database-injection: 4 이슈
• no-unsafe-deserialization: 4 이슈
• no-sql-injection: 3 이슈
• detect-non-literal-regexp: 3 이슈
• no-hardcoded-credentials: 2 이슈
• detect-eval-with-expression: 2 이슈
• no-weak-crypto: 2 이슈
... 및 18개의 추가 카테고리
security 규칙이 트리거됨:
• detect-non-literal-fs-filename: 5 이슈
• detect-object-injection: 5 이슈
• detect-child-process: 2 이슈
• detect-unsafe-regex: 2 이슈
... 및 6개의 추가 카테고리
Test 3 – False Positive Analysis
정밀도가 중요한 단계입니다. 저는 safe-patterns.js—안전하고 검증된 코드만 포함된 파일에 대해 두 플러그인을 모두 실행했습니다.
Results
| Plugin | False Positives | Precision |
|---|---|---|
secure-coding | 0 | 100 % |
security | 4 | 84 % |
The 4 False Positives from eslint-plugin-security
FP #1 – Validated key access (line 38)
// Pattern: Allowlist validation before access
const VALID_KEYS = ['name', 'email', 'age'];
function getField(obj, key) {
if (VALID_KEYS.includes(key)) {
return obj[key]; // ⚠️ security flags "Generic Object Injection Sink"
}
}
FP #2 – hasOwnProperty check (line 45)
// Pattern: Property existence check before access
function safeGet(obj, key) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return obj[key]; // ⚠️ security flags "Generic Object Injection Sink"
}
}
FP #3 – Guard clause with throw (line 153)
// Pattern: Early exit guard clause
const ALLOWED_THEMES = ['light', 'dark', 'system'];
function setTheme(userTheme) {
if (!ALLOWED_THEMES.includes(userTheme)) {
throw new Error('Invalid theme');
}
config[userTheme] = true; // ⚠️ security flags "Unsafe Assignment"
}
FP #4 – Timing‑safe comparison (line 212)
// Pattern: Timing‑safe equality check
if (crypto.timingSafeEqual(bufA, bufB)) {
// safe branch
}
FP #4: Path Validation (line 107)
// Pattern: basename + startsWith validation
function safeReadFile(userFilename) {
const safeName = path.basename(userFilename);
const safePath = path.join(SAFE_DIR, safeName);
if (!safePath.startsWith(SAFE_DIR)) {
throw new Error('Invalid path');
}
return fs.readFileSync(safePath); // ⚠️ security flags "non literal argument"
}
경로는 완전히 검증됩니다: basename이 경로 탐색을 제거하고, startsWith가 디렉터리를 확인합니다.
Why secure-coding Avoids These
우리는 AST‑기반 검증 탐지를 사용합니다:
| Pattern | Detection Method |
|---|---|
allowlist.includes(key) | if 문 안의 includes() 호출을 검사 |
hasOwnProperty(key) | hasOwnProperty / hasOwn 호출을 검사 |
Guard clause + throw | 조기 종료가 포함된 IfStatement를 탐지 |
startsWith() validation | 경로‑검증 패턴을 탐지 |
OWASP 적용 범위 비교
| 적용 범위 | secure-coding | security |
|---|---|---|
| OWASP Web Top 10 | 10/10 (100 %) | ~3/10 (~30 %) |
| OWASP Mobile Top 10 | 10/10 (100 %) | 0/10 (0 %) |
| 총계 | 20/20 | ~3/20 |
LLM/AI 메시지 비교
Security rules are increasingly consumed by AI coding assistants. Compare the messages:
eslint-plugin-security
Found child_process.exec() with non Literal first argument
eslint-plugin-secure-coding
🔒 CWE-78 OWASP:A03‑Injection CVSS:9.8 | Command injection detected | CRITICAL
Fix: Use execFile/spawn with {shell: false} and array args
📚 https://owasp.org/www-community/attacks/Command_Injection
| 기능 | secure-coding | security |
|---|---|---|
| CWE ID | ✅ | ❌ |
| OWASP 카테고리 | ✅ | ❌ |
| CVSS 점수 | ✅ | ❌ |
| 수정 지침 | ✅ | ❌ |
| 문서 링크 | ✅ | ❌ |
기능 및 문서 비교
| 기능 | secure-coding | security |
|---|---|---|
| 총 규칙 수 | 89 | 14 |
| 문서 | 포괄적 (규칙별) | 기본 |
| 수정 제안/규칙 | 3‑6개 제안 | 0 |
| CWE 참조 | ✅ 모든 규칙 | ❌ 없음 |
| CVSS 점수 | ✅ 예 | ❌ 아니오 |
| OWASP 매핑 | ✅ 웹 + 모바일 | ❌ 없음 |
| TypeScript 지원 | ✅ 전체 | ⚠️ 부분적 |
| Flat Config 지원 | ✅ Native | ✅ Native |
| 프리셋 | minimal, recommended, strict | recommended |
| 마지막 업데이트 | 활성 | 유지보수 모드 |
최종 평결
| 카테고리 | secure-coding | security | 승자 |
|---|---|---|---|
| 성능/문제 | 9.95 ms | 28.16 ms | 🟢 secure-coding |
| 탐지 | 80 issues | 21 issues | 🟢 secure-coding |
| 오탐 | 0 | 4 | 🟢 secure-coding |
| 정밀도 | 100 % | 84 % | 🟢 secure-coding |
| 전체 규칙 수 | 89 | 14 | 🟢 secure-coding |
| OWASP 커버리지 | 20/20 | ~3/20 | 🟢 secure-coding |
| 문서화 | 포괄적 | 기본 | 🟢 secure-coding |
| 수정 제안 | 3‑6 per rule | 0 | 🟢 secure-coding |
| LLM 최적화 | ⭐⭐⭐⭐⭐ | ⭐⭐ | 🟢 secure-coding |
핵심 인사이트
- Performance per issue matters –
secure-coding은 2.83× 더 효율적이며, 감지된 이슈당 성능이 중요합니다. - “Speed advantage” = detection gap – 기존 솔루션이 더 빠르게 보이는 이유는 취약점을 놓치기 때문입니다.
- 0 false positives – 표시된 모든 이슈는 실제 취약점입니다.
- 6× more rules – 89개의 규칙 대 14개로, 웹, 모바일, API, AI 보안을 모두 포괄합니다.
- Developer experience – 각 규칙에 CWE/OWASP 참조, CVSS 점수, 그리고 3‑6개의 해결 방안이 포함됩니다.
직접 해보기
npm install eslint-plugin-secure-coding --save-dev
// eslint.config.js
import secureCoding from 'eslint-plugin-secure-coding';
export default [secureCoding.configs.recommended];
벤치마크 코드는 오픈 소스입니다:
GitHub에서 벤치마크