당신의 ESLint 보안 플러그인이 80%의 취약점을 놓치고 있습니다 (증거가 있습니다)

발행: (2025년 12월 21일 오전 01:25 GMT+9)
8 min read
원문: Dev.to

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-codingsecurity승자
성능 / 이슈24.95 ms25.12 ms🟢 secure-coding
총 시간723.54 ms527.58 ms🔵 security
발견된 이슈2921🟢 secure-coding
탐지율138 %100 %🟢 secure-coding

규칙별 탐지

규칙 카테고리securitysecure-coding차이
타이밍 공격15+4 🟢
자식 프로세스24+2 🟢
비리터럴 정규식13+2 🟢
Eval / 코드 실행12+1 🟢
불충분한 난수성01+1 🟢
파일 시스템 경로 탐색55=
객체 주입55=
동적 Require22=
위험한 정규식22=
Buffer API20-2 🔵
총계2129+8

핵심 발견: 동일한 규칙 카테고리에서 secure-coding38 % 더 많은 이슈를 찾아내면서도 이슈당 거의 동일한 효율성을 유지합니다.

테스트 2 – 권장 프리셋

다음으로, 각 플러그인의 권장 설정(기본 제공 경험)을 테스트했습니다.

결과

측정항목secure-codingsecurity승자
성능 / 이슈9.95 ms28.16 ms🟢 secure-coding
총 시간795.99 ms591.41 ms🔵 security
발견된 이슈8021🟢 secure-coding
트리거된 규칙3010🟢 secure-coding
전체 규칙8914🟢 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

PluginFalse PositivesPrecision
secure-coding0100 %
security484 %

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‑기반 검증 탐지를 사용합니다:

PatternDetection Method
allowlist.includes(key)if 문 안의 includes() 호출을 검사
hasOwnProperty(key)hasOwnProperty / hasOwn 호출을 검사
Guard clause + throw조기 종료가 포함된 IfStatement를 탐지
startsWith() validation경로‑검증 패턴을 탐지

OWASP 적용 범위 비교

적용 범위secure-codingsecurity
OWASP Web Top 1010/10 (100 %)~3/10 (~30 %)
OWASP Mobile Top 1010/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-codingsecurity
CWE ID
OWASP 카테고리
CVSS 점수
수정 지침
문서 링크

기능 및 문서 비교

기능secure-codingsecurity
총 규칙 수8914
문서포괄적 (규칙별)기본
수정 제안/규칙3‑6개 제안0
CWE 참조✅ 모든 규칙❌ 없음
CVSS 점수✅ 예❌ 아니오
OWASP 매핑✅ 웹 + 모바일❌ 없음
TypeScript 지원✅ 전체⚠️ 부분적
Flat Config 지원✅ Native✅ Native
프리셋minimal, recommended, strictrecommended
마지막 업데이트활성유지보수 모드

최종 평결

카테고리secure-codingsecurity승자
성능/문제9.95 ms28.16 ms🟢 secure-coding
탐지80 issues21 issues🟢 secure-coding
오탐04🟢 secure-coding
정밀도100 %84 %🟢 secure-coding
전체 규칙 수8914🟢 secure-coding
OWASP 커버리지20/20~3/20🟢 secure-coding
문서화포괄적기본🟢 secure-coding
수정 제안3‑6 per rule0🟢 secure-coding
LLM 최적화⭐⭐⭐⭐⭐⭐⭐🟢 secure-coding

핵심 인사이트

  • Performance per issue matterssecure-coding2.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에서 벤치마크

Back to Blog

관련 글

더 보기 »