All projects

owasp-security-checker

★ 0 stars↓ 0 downloads
Open on GitHub ↗

README.md

OWASP Security Checker

image

A static code analyzer for OWASP Top 10:2025 vulnerabilities—A04 (Cryptographic Failures) and A07 (Identification and Authentication Failures). It works as a VS Code extension and as a CLI for CI/CD.

Supported languages (9): JavaScript, TypeScript (+ JSX/TSX), Python, Java, Kotlin, Go, PHP, C, C++. For JS/TS, it uses semantic AST analysis (TypeScript Compiler API); for other languages, it uses a regex engine with comment masking; an ML classifier for false positives is applied to the findings across all languages.

Version 0.2.0 includes three key improvements:

The ability to What this provides
AST analysis (TypeScript Compiler API) Accurate detection for JS/TS without false positives on comments and strings
ML classification (logistic regression) Context-based false positive suppression (placeholders, namespace URLs, test files)
GitHub Actions + CLI (SARIF) Automatic checking with every commit/PR

Architecture

src/
├── core/                  ← ядро анализа (НЕ зависит от vscode → работает и в CLI)
│   ├── engine.ts          ← оркестратор: выбор анализатора + ML
│   ├── astAnalyzer.ts     ← AST-детекторы для JS/TS (ts.createSourceFile)
│   ├── regexAnalyzer.ts   ← regex-движок для Python/Java/Kotlin/Go/PHP/C/C++
│   ├── text.ts            ← позиции, комментарии (лексер)
│   └── ml/
│       ├── features.ts    ← извлечение контекстных признаков
│       ├── classifier.ts  ← инференс (sigmoid(w·x))
│       ├── dataset.ts     ← размеченный обучающий набор
│       ├── train.ts       ← обучение (градиентный спуск + L2)
│       └── model.ts       ← обученные веса (генерируется)
├── rules.ts               ← база из 70+ правил (CWE/OWASP), 9 языков
├── diagnosticProvider.ts  ← адаптер ядра → vscode.Diagnostic
├── hoverProvider.ts · codeActionProvider.ts · reportPanel.ts
├── extension.ts           ← точка входа расширения
└── cli.ts                 ← headless-сканер (SARIF) для CI

The extension and CLI use the same core, so the results in the editor and in CI are identical.


How it works (full analysis pipeline)

Analysis of a single document goes through a single pipeline engine.analyze() — it is the same in both the editor and the CLI. Let’s break it down step by step.

Step 0. Input

The input is an object { code, languageId, filePath }:

  • code — the file’s text;
  • languageId — a VS Code-style language identifier (javascript, kotlin, python, …). In the editor, VS Code provides this; in the CLI, it’s derived from the file extension (.kt/.ktskotlin);
  • filePath — path, used as a contextual hint for ML (hits in test directories are downweighted).

First, computeLineStarts() it builds a table of line-start offsets—this allows it to O(log n) convert any offset into “line:column” coordinates, independent of the editor’s API.

Step 1. Select a parser by language

                     ┌──────────────────────────────────────────────┐
   JS / TS / JSX/TSX │  AST-детекторы (TypeScript Compiler API)      │
 ─────────────────►  │  + regex-fallback для правил без AST-детектора │
                     │    (матчи внутри комментариев отбрасываются)  │
                     └──────────────────────────────────────────────┘
   Kotlin, Python,   ┌──────────────────────────────────────────────┐
   Java, Go, PHP,    │  regex-движок + лексер комментариев (text.ts) │
   C, C++       ───► │  (матчи внутри // … и /* … */ отбрасываются)  │
                     └──────────────────────────────────────────────┘
  • JS/TS: the code is parsed into an AST (ts.createSourceFile). Semantic detectors (see below) look for node patterns rather than text. Rules for which there is no AST detector (AES-ECB, DES, RC4, etc.) are handled by the regex engine, but matches within comment ranges obtained from ts.createScannerare discarded.
  • Other languages (including Kotlin): the regex engine is used runRegexRules(). Before that, a lightweight lexer findCommentRanges() identifies comment ranges (//, /* */, and for Python/PHP— #), tracking string literals so that // they aren’t mistaken for comments within a string. Any match that begins inside a comment is ignored.

At this step, each rule that matches generates a “raw” hit Finding with coordinates, match text, and an engine flag (ast | regex).

Step 2. Filtering by Rules and Severity

Even before the engines are launched, ruleAllowed(): the following are taken into account disabledRules, enabledRules and minSeverity. This allows you to enable or disable individual rules and raise the severity threshold at the configuration level.

Step 3. ML scoring of each hit

For each raw hit, a vector of 12 contextual features (features.ts), and logistic regression produces P(уязвимость):

признаки(находка, строка, путь) → P = σ(w · x) → сравнение с порогом (0.5)

The features are grouped by rule type: value-carrying features (entropy, length, placeholder, namespace-URL) are calculated only for secret rules (*-hardcoded-secret) and HTTP rules (*-http-url). Therefore, for example, kotlin-hardcoded-secret both kotlin-http-url automatically receive “meaningful” features—precisely because of the uniform naming convention for rules, supporting a new language does not require changing the ML code.

If P < порога, the match is marked suppressed and is hidden by default (in the CLI, it can be shown with the flag --include-suppressed; in the editor, via the setting showSuppressedAsHints).

Step 4. Result

The remaining hits are sorted by position and returned. Next:

  • in the editor diagnosticProvider.ts converts them to vscode.Diagnostic (with the engine tag and % confidence);
  • in the CLI, they are printed and/or exported to SARIF 2.1.0 for GitHub Code Scanning.

Adding Support for a New Language (Using Kotlin as an Example)

  1. Define rules with languages: ['kotlin'] in rules.ts. For secret/URL rules, follow the naming convention (kotlin-hardcoded-secret, kotlin-http-url) so that ML features are enabled automatically.
  2. Register kotlin in SUPPORTED_LANGUAGES (extension.ts) and map extensions .kt/.kts in cli.ts.
  3. (Optional) add the language to the general rules (for example, jwt-alg-none).
  4. Expand the training set dataset.ts with examples in the new language and run npm run train.

No need to write an AST parser: Kotlin, like Java/Go/PHP, is handled by the regex engine with comment masking.


How AST analysis works

For JavaScript/TypeScript, the code is parsed by the TypeScript parser into a tree (AST). Detectors match semantic forms, not text:

  • crypto.createHash('md5') only a mention within // комментарии or within a string "...createHash('md5')..." is ignored;
  • res.cookie(..., { httpOnly: true, secure: true }) not highlighted, because the flags are actually present in the object;
  • { alg: 'none' }, origin: '*', rejectUnauthorized: false are found as tree nodes.

Other languages use a regex engine with comment masking (the lexer in text.ts).

How the ML classifier works

Each match is converted into a vector of contextual features (features.ts): rule type, severity, entropy, and value length; placeholder features; context reading; test path; namespace URL; and more. Logistic regression calculates the probability that the match is a genuine vulnerability. Findings below the threshold (mlThreshold, default 0.5) are hidden.

Retraining the model:

npm run train     # компилирует проект и пересчитывает src/core/ml/model.ts

How to see how ML works

By default, ML hides false positives, so its effect is not immediately apparent. To see it:

  1. In the editor, enable owaspChecker.showSuppressedAsHints = true: suppressed detections will appear in gray with the label “🤖 Suppressed by ML (confidence N%)”.
  2. In the console—run the CLI with --include-suppressed (confidence percentages are visible):
node ./out/cli.js demo/ml-secrets.js --include-suppressed

Sample files where true and false positives look identical for a regex, but ML distinguishes between them:

File What it shows Result
demo/ml-secrets.js Real secrets vs. placeholders/low entropy 4 retained (91–94%), 7 suppressed (3–7%)
demo/ml-urls.ts Real HTTP traffic vs. XML namespace 3 left (91%), 6 suppressed (12%)
demo/ml-context.py Secrets in code vs. placeholders 3 left (90–93%), 5 suppressed (3–7%)

CLI and GitHub Action

npm run compile
node ./out/cli.js <пути...> [опции]

  --sarif <file>          SARIF 2.1.0 отчёт (для GitHub Code Scanning)
  --min-severity <sev>    critical|high|medium|low (по умолчанию low)
  --fail-on <sev>         выход с кодом 1 при находке ≥ уровня (по умолчанию high)
  --no-ml                 отключить ML-классификатор
  --include-suppressed    показать и подавленные находки
  --json | --quiet

GitHub Action (.github/workflows/security-scan.yml in the repository root) runs on every push/PR, generates a SARIF, and uploads it to Security → Code scanning; it fails on CRITICAL/HIGH.

Pre-commit hook (.githooks/pre-commit):

git config core.hooksPath .githooks

Testing

npm test          # 24 теста (node:test): AST, ML, движок, метрики, Kotlin

Accuracy comparison using a set of false positives (demo/false-positives.js):

Method Detections Reference Precision
Regex (naive) 11 2 18%
AST 7 2 29%
AST + ML 2 2 100%

Compilation and Installation

npm install
npm run compile
npm run package          # → owasp-security-checker-0.2.0.vsix
code --install-extension owasp-security-checker-0.2.0.vsix

Extension Settings

Parameter Default Description
owaspChecker.enabled true Auto-analysis on opening/saving
owaspChecker.analyzeOnType true Live analysis during input, without saving
owaspChecker.analyzeOnTypeDelay 400 Delay (ms) before re-analysis
owaspChecker.minSeverity low Minimum level
owaspChecker.useMlClassifier true ML false positive suppression
owaspChecker.mlThreshold 0.5 ML confidence threshold
owaspChecker.showSuppressedAsHints false Show suppressed ML findings as hints
owaspChecker.disabledRules / enabledRules [] Rule management

Releases

No releases yet.

Open issues

No open issues 🎉

This page was machine-translated from Russian

RU