From 9a2b55290440bebe9b113ee98740115c7eb6d824 Mon Sep 17 00:00:00 2001 From: batuthzcode Date: Tue, 18 Aug 2026 16:41:38 +0300 Subject: [PATCH 1/3] feat(static-analyzer): add severity failure thresholds --- src/static_analyzer/cli.py | 13 ++++++++- src/static_analyzer/runner.py | 43 ++++++++++++++++++++++++++++-- tests/test_cli.py | 37 +++++++++++++++++++++++++- tests/test_runner.py | 50 ++++++++++++++++++++++++++++++++++- 4 files changed, 138 insertions(+), 5 deletions(-) diff --git a/src/static_analyzer/cli.py b/src/static_analyzer/cli.py index dc3e718..6964a42 100644 --- a/src/static_analyzer/cli.py +++ b/src/static_analyzer/cli.py @@ -14,6 +14,7 @@ class CliArguments: target: Path output_format: str + fail_on: str = "any" def build_parser() -> argparse.ArgumentParser: @@ -40,6 +41,15 @@ def build_parser() -> argparse.ArgumentParser: help="Output format to use. Available formats: text, json.", ) + parser.add_argument( + "--fail-on", + choices=("any", "info", "warning", "error"), + default="any", + help=( + "Return exit code 1 for findings at or above this severity." + ), + ) + return parser @@ -54,4 +64,5 @@ def parse_arguments( return CliArguments( target=namespace.target, output_format=namespace.output_format, - ) \ No newline at end of file + fail_on=namespace.fail_on, + ) diff --git a/src/static_analyzer/runner.py b/src/static_analyzer/runner.py index b5dbf8c..0e5ceca 100644 --- a/src/static_analyzer/runner.py +++ b/src/static_analyzer/runner.py @@ -12,6 +12,7 @@ format_findings_json, format_findings_text, ) +from static_analyzer.models import Finding, Severity from static_analyzer.project_analyzer import ProjectAnalyzer @@ -24,6 +25,12 @@ UnicodeDecodeError, ) +_SEVERITY_RANK = { + Severity.INFO: 1, + Severity.WARNING: 2, + Severity.ERROR: 3, +} + def run_cli( argv: Sequence[str] | None = None, @@ -62,7 +69,10 @@ def run_cli( output_stream.write(output) output_stream.write("\n") - return 1 if findings else 0 + return _calculate_exit_code( + findings, + arguments.fail_on, + ) def main( @@ -88,4 +98,33 @@ def main( ) except _OPERATIONAL_ERRORS as error: error_stream.write(f"Error: {error}\n") - return 2 \ No newline at end of file + return 2 + + +def _calculate_exit_code( + findings: list[Finding], + fail_on: str, +) -> int: + """Return whether findings meet the configured severity threshold.""" + + if not findings: + return 0 + + if fail_on == "any": + return 1 + + try: + minimum_severity = Severity(fail_on) + minimum_rank = _SEVERITY_RANK[minimum_severity] + except (ValueError, KeyError) as error: + raise ValueError( + f"Unsupported fail-on severity: {fail_on}" + ) from error + + return int( + any( + _SEVERITY_RANK[finding.severity] + >= minimum_rank + for finding in findings + ) + ) diff --git a/tests/test_cli.py b/tests/test_cli.py index d39f8df..ba8061b 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -19,10 +19,12 @@ def test_cli_arguments_store_expected_fields() -> None: arguments = CliArguments( target=Path("src"), output_format="json", + fail_on="warning", ) assert arguments.target == Path("src") assert arguments.output_format == "json" + assert arguments.fail_on == "warning" def test_cli_arguments_are_immutable() -> None: @@ -43,6 +45,7 @@ def test_cli_arguments_use_slots() -> None: assert CliArguments.__slots__ == ( "target", "output_format", + "fail_on", ) @@ -154,6 +157,26 @@ def test_json_format_is_accepted() -> None: assert arguments.output_format == "json" +def test_default_fail_on_level_is_any() -> None: + """Every finding should fail unless a severity floor is selected.""" + + assert parse_arguments(["src"]).fail_on == "any" + + +@pytest.mark.parametrize( + "fail_on", + ["any", "info", "warning", "error"], +) +def test_supported_fail_on_level_is_accepted(fail_on: str) -> None: + """All documented static severity thresholds should parse.""" + + arguments = parse_arguments( + ["src", "--fail-on", fail_on] + ) + + assert arguments.fail_on == fail_on + + def test_format_option_can_appear_before_target() -> None: """Optional arguments should work before the target.""" @@ -207,6 +230,15 @@ def test_invalid_output_format_exits_with_usage_error() -> None: assert error.value.code == 2 +def test_invalid_fail_on_level_exits_with_usage_error() -> None: + """An unsupported static severity threshold should be rejected.""" + + with pytest.raises(SystemExit) as error: + parse_arguments(["src", "--fail-on", "critical"]) + + assert error.value.code == 2 + + def test_unknown_argument_exits_with_usage_error() -> None: """Unknown arguments should be rejected.""" @@ -247,9 +279,12 @@ def test_help_output_contains_expected_information( assert "securecode-analyzer" in output assert "target" in output assert "--format" in output + assert "--fail-on" in output assert "text" in output assert "json" in output + assert "warning" in output + assert "error" in output assert ( "Analyze Python source code for quality and security findings." in output - ) \ No newline at end of file + ) diff --git a/tests/test_runner.py b/tests/test_runner.py index f97357f..d3e4a06 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -385,6 +385,54 @@ def test_error_finding_returns_exit_code_one() -> None: assert exit_code == 1 +@pytest.mark.parametrize( + ("fail_on", "severity", "expected_exit_code"), + [ + ("info", Severity.INFO, 1), + ("info", Severity.WARNING, 1), + ("warning", Severity.INFO, 0), + ("warning", Severity.WARNING, 1), + ("warning", Severity.ERROR, 1), + ("error", Severity.WARNING, 0), + ("error", Severity.ERROR, 1), + ], +) +def test_fail_on_threshold_uses_severity_order( + fail_on: str, + severity: Severity, + expected_exit_code: int, +) -> None: + """Static findings should use the configured severity floor.""" + + _, factory = _factory_for([_finding(severity=severity)]) + + exit_code = run_cli( + ["src", "--fail-on", fail_on], + analyzer_factory=factory, + stdout=io.StringIO(), + ) + + assert exit_code == expected_exit_code + + +def test_fail_on_threshold_considers_every_finding() -> None: + """A later finding at the threshold should still fail analysis.""" + + findings = [ + _finding(severity=Severity.INFO), + _finding(severity=Severity.ERROR), + ] + _, factory = _factory_for(findings) + + exit_code = run_cli( + ["src", "--fail-on", "error"], + analyzer_factory=factory, + stdout=io.StringIO(), + ) + + assert exit_code == 1 + + def test_main_handles_file_not_found_error() -> None: """Missing targets should be presented as operational errors.""" @@ -622,4 +670,4 @@ def test_console_script_points_to_runner_main() -> None: assert project_data["project"]["scripts"][ "securecode-analyzer" - ] == "static_analyzer.runner:main" \ No newline at end of file + ] == "static_analyzer.runner:main" From a68d549a75548e3090c917e3b471e56409a2444d Mon Sep 17 00:00:00 2001 From: batuthzcode Date: Tue, 18 Aug 2026 16:41:38 +0300 Subject: [PATCH 2/3] docs(project): add installation and usage guide --- README.md | 424 ++++++++++++++++++++-- docs/README.md | 1 + docs/ci.md | 2 +- docs/components/static-analyzer/README.md | 65 +++- docs/project-plan.md | 4 + docs/self-analysis.md | 2 +- 6 files changed, 458 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index eced64b..161f66b 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,413 @@ -## [Components](docs/components/README.md) +# SecureCode Analyzer -[Üç bileşenin genel listesini görüntüle](docs/components/README.md) +[![CI](https://github.com/batuthzcode/securecode-analyzer/actions/workflows/ci.yml/badge.svg)](https://github.com/batuthzcode/securecode-analyzer/actions/workflows/ci.yml) -### 1. Static Code Analyzer +SecureCode Analyzer, Python projeleri için iki bağımsız komut satırı aracı ve +bu araçların kontrollü olarak gösterildiği küçük bir Flask uygulaması içerir: -Python kaynak kodlarını çalıştırmadan inceleyerek temel kod kalitesi ve güvenlik problemlerini tespit eder. +- Python kaynaklarını AST ve metin kurallarıyla inceleyen static analyzer +- Exact-pinned Python bağımlılıklarını OSV verisiyle eşleştiren vulnerability + scanner +- Gerçek credential içermeyen, çalışan uygulamadan izole güvenlik demo + fixture'ları -[Static Code Analyzer bileşenini incele](docs/components/static-analyzer/README.md) +Proje Python `3.11+` destekler. Mevcut doğrulama paketi 1.065 test, `%98,6` +birleşik statement/branch coverage, whole-project self-analysis ve offline OSV +entegrasyon kontrolü içerir. -### 2. Dependency and CVE Scanner +## Özellikler -Python projelerindeki bağımlılıkları inceleyerek bilinen güvenlik açıklarını tespit eder. +- Alt dizinleri özyinelemeli tarayan Python dosya keşfi +- Altı yerleşik static-analysis kuralı +- İnsan tarafından okunabilir text ve makine tarafından okunabilir JSON çıktı +- CI kullanımına uygun `0`, `1` ve `2` exit code sözleşmesi +- Static analyzer için `any`, `info`, `warning`, `error` severity eşikleri +- Dependency scanner için `any`, `low`, `medium`, `high`, `critical` eşikleri +- PyPI paketleri için canlı OSV query desteği +- CVSS v3 base-score hesaplama ve qualitative severity sınıflandırması +- Deterministik offline OSV fixture akışı +- GitHub Actions test, coverage, static-analysis ve dependency-scan gate'leri +- Canonical JSON raporlar ve 14 günlük Actions artifact'ları -[Dependency and CVE Scanner bileşenini incele](docs/components/dependency-scanner/README.md) +## Mimari -### 3. Sample Web Application +```text +Python source directory + -> FileScanner + -> SourceReader + -> AnalysisEngine + -> SA001 ... SA006 + -> text / JSON report -Statik kod analiz aracının ve bağımlılık tarayıcısının test edileceği örnek Flask uygulamasıdır. +requirements.txt + -> requirements parser + -> DependencyScanner + -> OsvVulnerabilitySource + -> OsvQueryClient + -> text / JSON report +``` -[Sample Web Application bileşenini incele](docs/components/sample-web-app/README.md) +Üç ana bileşenin ayrıntılı dokümanları: -## Documentation +- [Static Code Analyzer](docs/components/static-analyzer/README.md) +- [Dependency Scanner](docs/components/dependency-scanner/README.md) +- [Sample Web Application](docs/components/sample-web-app/README.md) -[Proje dokümantasyonunu görüntüle](docs/README.md) +## Static Analysis Kuralları -### Genel Proje Dokümanları +| Rule ID | Kural | Varsayılan severity | Davranış | +|---|---|---|---| +| `SA001` | Long Function | `WARNING` | 50 satırı aşan fonksiyonları bulur. | +| `SA002` | Long Class | `WARNING` | 200 satırı aşan sınıfları bulur. | +| `SA003` | TODO/FIXME | `INFO` / `WARNING` | Yorumlardaki TODO ve FIXME işaretlerini bulur. | +| `SA004` | Empty Except | `WARNING` | Yalnızca `pass` içeren exception handler'ları bulur. | +| `SA005` | Hardcoded Secret | `WARNING` | Hassas isimli değişkenlere atanan string literal değerleri işaretler. | +| `SA006` | Naming Convention | `INFO` | Fonksiyonlarda `snake_case`, sınıflarda `PascalCase` kontrol eder. | -- [Proje Kapsamı](docs/scope.md) -- [Beş Haftalık Proje Planı](docs/project-plan.md) -- [GitHub Actions CI](docs/ci.md) -- [Whole-Project Self-Analysis ve Test Coverage](docs/self-analysis.md) +`SA005` secret değerini rapora yazmaz; yalnızca genel bir bulgu mesajı üretir. -### Bileşen Dokümanları +## Kurulum -- [Static Code Analyzer](docs/components/static-analyzer/README.md) -- [Dependency Scanner](docs/components/dependency-scanner/README.md) -- [Sample Web Application](docs/components/sample-web-app/README.md) +### 1. Repository'yi klonla + +```text +git clone https://github.com/batuthzcode/securecode-analyzer.git +cd securecode-analyzer +``` + +### 2. Sanal ortam oluştur + +```text +python -m venv .venv +``` + +Windows Command Prompt aktivasyonu: + +```bat +.venv\Scripts\activate.bat +``` + +Windows PowerShell aktivasyonu: + +```powershell +.\.venv\Scripts\Activate.ps1 +``` + +Linux ve macOS aktivasyonu: + +```bash +source .venv/bin/activate +``` + +### 3. Paketi kur + +Yalnızca iki analiz aracını kurmak için: + +```text +python -m pip install --upgrade pip +python -m pip install -e . +``` + +Test, coverage ve sample app bağımlılıklarıyla geliştirme kurulumu: + +```text +python -m pip install -e ".[dev]" +``` + +Yalnızca Flask demo bağımlılığını eklemek için `.[sample-app]` extra değeri de +kullanılabilir. Kurulumu kontrol et: + +```text +securecode-analyzer --help +securecode-dependency-scan --help +``` + +## Static Analyzer Kullanımı + +### Temel tarama + +Bir Python kaynak klasörünü text formatında tara: + +```text +securecode-analyzer src +``` + +Repository'nin tamamını tara: + +```text +securecode-analyzer . +``` + +### JSON çıktı + +```text +securecode-analyzer src --format json +``` + +CLI çıktıyı stdout'a yazar. JSON raporunu dosyada saklamak için shell +redirection kullanılabilir: + +```powershell +New-Item -ItemType Directory -Force reports\local +securecode-analyzer src --format json > reports\local\static-analysis.json +``` + +### Severity gate + +Varsayılan `--fail-on any`, severity değerinden bağımsız olarak her bulguda +exit code `1` üretir. Yalnızca `WARNING` veya `ERROR` bulgularında başarısız +olmak için: + +```text +securecode-analyzer src --fail-on warning +``` + +Yalnızca `ERROR` bulgularını pipeline hatası yapmak için: + +```text +securecode-analyzer src --format json --fail-on error +``` + +Severity sırası: + +```text +info < warning < error +``` + +### Parametreler + +| Parametre | Zorunlu | Varsayılan | Açıklama | +|---|---:|---|---| +| `target` | Evet | — | Analiz edilecek Python kaynak klasörü. | +| `--format` | Hayır | `text` | `text` veya `json`. | +| `--fail-on` | Hayır | `any` | `any`, `info`, `warning` veya `error`. | +| `-h`, `--help` | Hayır | — | CLI yardımını gösterir. | + +### Örnek terminal çıktısı + +```text +[WARNING] SA005 src/example.py:1:1 - Possible hardcoded secret found. + +1 finding found. +``` + +Temiz tarama çıktısı: + +```text +No findings found. +``` + +### Exit code sözleşmesi + +| Exit code | Anlam | +|---:|---| +| `0` | Analiz tamamlandı ve seçilen eşiği karşılayan bulgu yok. | +| `1` | Analiz tamamlandı ve seçilen eşiği karşılayan bulgu var. | +| `2` | Hedef, dosya okuma, Unicode veya Python syntax hatası oluştu. | + +Rapor, threshold altında kalan bulguları da göstermeye devam eder; `--fail-on` +yalnızca process exit code değerini değiştirir. -## Current Status +## Dependency Scanner Kullanımı -Birinci hafta kapsamında aşağıdaki çalışmalar yapılmıştır: +Dependency scanner yalnızca exact pin biçimindeki aktif satırları kabul eder: -* Proje kapsamı hazırlandı. -* Fonksiyonel ve fonksiyonel olmayan gereksinimler belirlendi. -* Teknik tasarım dokümanı oluşturuldu. -* Beş haftalık proje planı hazırlandı. -* Proje üç ana bileşene ayrıldı. -* AST kullanılarak uzun fonksiyon tespiti yapan ilk prototip geliştirildi. +```text +Flask==3.1.3 +``` -## Development Workflow +### Canlı OSV taraması + +```text +securecode-dependency-scan requirements.txt +``` + +Bu komut `https://api.osv.dev/v1/query` endpoint'ine her dependency için +package, `PyPI` ecosystem ve version bilgisiyle POST isteği gönderir. + +### JSON çıktı ve rapor dosyası + +```text +securecode-dependency-scan requirements.txt --format json +``` + +CLI'ın kendi `--output` seçeneğiyle UTF-8 rapor yaz: + +```powershell +New-Item -ItemType Directory -Force reports\local +securecode-dependency-scan requirements.txt ` + --format json ` + --output reports\local\dependency-scan.json +``` + +Output dosyasının parent klasörü önceden bulunmalıdır. Input requirements +dosyasının output hedefi olarak seçilmesi güvenlik amacıyla reddedilir. + +### Severity gate + +Varsayılan politika bütün bulgularda başarısız olur: + +```text +securecode-dependency-scan requirements.txt --fail-on any +``` + +Yalnızca `HIGH` ve `CRITICAL` bulguları gate hatası yapmak için: + +```text +securecode-dependency-scan requirements.txt --fail-on high +``` + +Severity sırası: + +```text +unknown | low < medium < high < critical +``` + +`UNKNOWN` yalnızca `--fail-on any` politikasıyla exit code `1` üretir. + +### Parametreler + +| Parametre | Zorunlu | Varsayılan | Açıklama | +|---|---:|---|---| +| `requirements_file` | Evet | — | Taranacak UTF-8 requirements dosyası. | +| `--format` | Hayır | `text` | `text` veya `json`. | +| `--output` | Hayır | stdout | Raporun yazılacağı mevcut parent'a sahip dosya yolu. | +| `--fail-on` | Hayır | `any` | `any`, `low`, `medium`, `high` veya `critical`. | +| `--source` | Hayır | `osv` | Vulnerability source; şu anda yalnızca `osv`. | +| `--timeout` | Hayır | `10.0` | Pozitif ve finite OSV timeout süresi. | +| `-h`, `--help` | Hayır | — | CLI yardımını gösterir. | + +### Örnek terminal çıktısı + +```text +[HIGH] PYSEC-2024-38 fastapi==0.109.0 sample_app/requirements-vulnerable.txt:6 - Affected dependency. | source=OSV | fixed=0.109.1 | aliases=CVE-2024-24762 + +1 dependency scanned. 1 finding. 0 lookup errors. +``` + +Gerçek OSV mesajı ve alias listesi upstream advisory verisine göre daha uzun +olabilir. + +### Yerel OSV verisi + +Canlı OSV verisi zaman içinde değişebilir ve ağ erişimi gerektirir. Repository, +CI ve demo için provenance bilgisi içeren deterministik bir fixture sağlar: + +```powershell +python -m tools.run_ci_dependency_scan ` + --requirements sample_app/requirements-vulnerable.txt ` + --fixture tests/fixtures/osv/fastapi-0.109.0.json ` + --output reports/ci/dependency-scan.json ` + --fail-on critical +``` + +Bu yardımcı araç production requirements parser, dependency scanner, OSV +response parser, source mapping ve JSON formatter katmanlarını kullanır; HTTP +isteği göndermez. Fixture yalnızca kaydedilmiş `fastapi==0.109.0` sorgusunu +cevaplar ve genel amaçlı offline vulnerability database değildir. + +### Exit code sözleşmesi + +| Exit code | Anlam | +|---:|---| +| `0` | Tarama tamamlandı ve seçilen eşiği karşılayan bulgu yok. | +| `1` | Tarama tamamlandı ve seçilen eşiği karşılayan bulgu var. | +| `2` | Dosya, parse, output, OSV network/response veya lookup hatası var. | + +Bir lookup hatası oluşursa exit code `2`, finding eşiğinden önceliklidir. +Başarılı ve başarısız lookup kayıtları aynı kısmi raporda korunur. + +## Sample Web Application + +Flask demo uygulamasını repository kökünden çalıştır: + +```text +python -m pip install -e ".[sample-app]" +flask --app sample_app run --debug +``` + +Ardından `http://127.0.0.1:5000` adresini aç. Uygulama in-memory task CRUD +akışı sağlar; veriler process kapandığında silinir. + +Kontrollü güvenlik demosu: + +```text +securecode-analyzer sample_app --format text +securecode-dependency-scan sample_app/requirements-vulnerable.txt --fail-on high +``` + +Static komut tam olarak beş kasıtlı bulgu, dependency komutu canlı OSV verisi +uygunsa `PYSEC-2024-38` kaydını üretir. Vulnerable FastAPI pini normal Flask +runtime requirements dosyasından ayrıdır ve uygulamayı çalıştırmak için +kurulmaz. + +Deterministik demo raporlarını kontrol et: + +```text +python -m tools.generate_sample_app_reports --check +python -m tools.generate_self_analysis_report --check +``` + +## Test ve CI + +Tam paketi branch coverage gate'iyle çalıştır: + +```text +python -m coverage run -m pytest -q +python -m coverage report +``` + +CI minimum birleşik statement/branch coverage değerini `%97` olarak zorunlu +tutar. GitHub Actions Pull Request ve `main` push olaylarında şu job'ları +çalıştırır: + +1. Python 3.11 test ve coverage gate'i +2. Static analyzer kaynak, araç, demo ve whole-project report kontrolü +3. Offline OSV dependency vulnerability gate'i + +Static ve dependency JSON raporları Actions artifact'ı olarak 14 gün saklanır. +Ayrıntılar için [GitHub Actions CI](docs/ci.md) dokümanına bakın. + +## Bilinen Sınırlamalar + +- Static analyzer yalnızca Python `.py` dosyalarını ve klasör hedeflerini + destekler; JavaScript, Java, Go ve diğer diller kapsam dışıdır. +- Kurallar seçilmiş AST/metin kalıplarına dayanır. Dinamik veri akışı, + interprocedural taint analysis ve runtime davranışı analiz edilmez. +- TODO/FIXME, naming ve hardcoded-secret kuralları heuristic olduğu için false + positive veya false negative üretebilir. Finding'ler insan incelemesi + gerektirir. +- Inline suppression, özel config dosyası, autofix, SARIF ve HTML raporu yoktur. +- Requirements parser yalnızca `package==version` biçimini, boş satırları ve + tam satır yorumlarını destekler. Version range, environment marker, extras, + recursive include, editable, VCS ve URL requirement değerleri desteklenmez. +- Dependency scanner yalnızca Python/PyPI paketlerini ve OSV source değerini + destekler; `pyproject.toml`, lock file ve installed-environment keşfi yapmaz. +- Canlı tarama OSV API erişilebilirliğine ve mutable upstream veriye bağlıdır; + retry ve cache uygulanmaz. +- OSV kaydında geçerli CVSS v3 vector yoksa severity `UNKNOWN`, eşleşen + ecosystem fixed event yoksa güvenli sürüm bilgisi `null` kalabilir. +- Offline fixture yalnızca test ve demo için sabit bir OSV projection'dır; + güncel güvenlik taramasının yerine geçmez. +- Sample app authentication, kalıcı database ve production deployment + hardening sağlamaz. + +## Güvenlik Notu + +Bu araç eğitim ve temel otomasyon amacıyla geliştirilmiştir. Tek başına code +review, SAST/DAST platformu, dependency lock denetimi veya profesyonel +penetration testinin yerini almaz. Secret, token veya özel repository içeren +raporları paylaşmadan önce içeriği inceleyin. + +## Dokümantasyon + +- [Proje dokümantasyonu](docs/README.md) +- [Proje kapsamı](docs/scope.md) +- [Beş haftalık proje planı](docs/project-plan.md) +- [Whole-project self-analysis ve coverage](docs/self-analysis.md) +- [GitHub Actions CI](docs/ci.md) -Her geliştirme ayrı bir branch üzerinde yapılmaktadır. +## Geliştirme Akışı -Değişiklikler Pull Request üzerinden incelenmekte ve reviewer tarafından ana branch ile birleştirilmektedir. +Değişiklikler ayrı branch'lerde hazırlanır, test ve güvenlik gate'lerinden +geçen Pull Request'ler review sonrasında `main` branch'ine squash merge edilir. diff --git a/docs/README.md b/docs/README.md index 001093f..cadffcc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,6 +4,7 @@ Bu klasörde projenin genel dokümanları ve üç ana bileşene ait dokümanlar ## Genel Proje Dokümanları +- [Kurulum ve Kullanım](../README.md#kurulum) - [Proje Kapsamı](scope.md) - [Beş Haftalık Proje Planı](project-plan.md) - [GitHub Actions CI](ci.md) diff --git a/docs/ci.md b/docs/ci.md index aedb012..aff1978 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -266,7 +266,7 @@ Doğrulanan mevcut sonuç: CI workflow contract tests: 18 passed Offline dependency CI tests: 21 passed Python 3.11 compatibility tests: 1 passed -Complete test suite: 1044 passed +Complete test suite: 1065 passed Combined statement/branch coverage: 98.6% (97.0% required) Whole-project self-analysis check: passed (5 intentional findings) Workflow YAML parse check: passed diff --git a/docs/components/static-analyzer/README.md b/docs/components/static-analyzer/README.md index 3cfb613..6d4ac64 100644 --- a/docs/components/static-analyzer/README.md +++ b/docs/components/static-analyzer/README.md @@ -788,7 +788,7 @@ from static_analyzer.runner import main, run_cli 3. Hedef klasörü analiz eder. 4. Bulguları seçilen çıktı formatına dönüştürür. 5. Raporu standart çıktıya yazar. -6. Analiz sonucuna uygun exit code döndürür. +6. `--fail-on` severity eşiğine uygun exit code döndürür. Temel kullanım: @@ -808,6 +808,18 @@ exit_code = run_cli( ) ``` +Severity eşiği: + +```python +exit_code = run_cli( + [ + "src", + "--fail-on", + "warning", + ] +) +``` + Runner mevcut bileşenleri kullanır: ```python @@ -930,7 +942,7 @@ Bulgusuz analiz: 0 ``` -Bir veya daha fazla bulgu: +Seçilen `--fail-on` eşiğini karşılayan bir veya daha fazla bulgu: ```text 1 @@ -942,7 +954,8 @@ Beklenen operasyonel hata: 2 ``` -Bu sürümde bütün severity seviyeleri bulgu exit code değerini üretir: +Varsayılan `--fail-on any` bütün severity seviyelerinde bulgu exit code +değerini üretir: ```text INFO @@ -952,6 +965,19 @@ ERROR Bir bulgu yalnızca `INFO` seviyesinde olsa bile exit code `1` olur. +Desteklenen eşikler: + +```text +any +info +warning +error +``` + +`--fail-on warning`, `INFO` bulgularını raporda korur ancak yalnızca `WARNING` +ve `ERROR` bulgularında exit code `1` üretir. `--fail-on error` yalnızca +`ERROR` bulgularında başarısız olur. + #### Operasyonel Hatalar `main()` aşağıdaki beklenen hataları yönetir: @@ -1025,12 +1051,14 @@ bir veri sınıfıdır: class CliArguments: target: Path output_format: str + fail_on: str = "any" ``` Alanlar: - `target`: Analiz edilecek hedef klasörün `Path` karşılığı - `output_format`: `text` veya `json` +- `fail_on`: `any`, `info`, `warning` veya `error` #### Hedef yol @@ -1072,6 +1100,26 @@ Geçersiz bir format standart `argparse` kullanım hatası üretir: securecode-analyzer src --format xml ``` +#### Fail-on eşiği + +Varsayılan eşik: + +```text +any +``` + +Severity gate örneği: + +```powershell +securecode-analyzer src --fail-on warning +``` + +Eşik sırası: + +```text +info < warning < error +``` + #### Parser oluşturma Her `build_parser()` çağrısı yeni bir `ArgumentParser` nesnesi üretir: @@ -1101,11 +1149,18 @@ Argümanlar doğrudan bir liste üzerinden ayrıştırılabilir: ```python arguments = parse_arguments( - ["src", "--format", "json"] + [ + "src", + "--format", + "json", + "--fail-on", + "warning", + ] ) assert arguments.target == Path("src") assert arguments.output_format == "json" +assert arguments.fail_on == "warning" ``` `argv=None` kullanıldığında mevcut process argümanları ayrıştırılır. @@ -1544,4 +1599,4 @@ Henüz tamamlanmayan çalışmalar: ## 17. Navigation - [Tüm bileşenlere dön](../README.md) -- [Projenin ana sayfasına dön](../../../README.md) \ No newline at end of file +- [Projenin ana sayfasına dön](../../../README.md) diff --git a/docs/project-plan.md b/docs/project-plan.md index df397eb..dd135f1 100644 --- a/docs/project-plan.md +++ b/docs/project-plan.md @@ -1179,6 +1179,10 @@ Sub-task’ler: * API bağımlılığını yaz. * Güvenli sürüm bilgisinin her zaman bulunmayabileceğini yaz. +**Durum:** Tamamlandı. Cross-platform kurulum, iki CLI'ın text/JSON ve +severity-gate kullanımı, canlı/yerel OSV ayrımı, exit code sözleşmeleri ve +bilinen sınırlamalar root [`README.md`](../README.md) içinde belgelenmiştir. + ## Backlog 5.7 — Son teknik dokümantasyon **Öncelik:** P1 diff --git a/docs/self-analysis.md b/docs/self-analysis.md index 24fd88d..22030af 100644 --- a/docs/self-analysis.md +++ b/docs/self-analysis.md @@ -66,7 +66,7 @@ Dosya static-analysis ve integration test kapsamından çıkarılmaz. eklenen testlerden sonra doğrulanan sonuç: ```text -Tests: 1044 passed +Tests: 1065 passed Combined statement/branch coverage: 98.6% Required CI floor: 97.0% ``` From f38669152df9f7674e9093a9dc9a529c2139b1af Mon Sep 17 00:00:00 2001 From: batuthzcode Date: Tue, 18 Aug 2026 16:41:38 +0300 Subject: [PATCH 3/3] test(docs): validate README contract --- tests/test_readme.py | 113 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 tests/test_readme.py diff --git a/tests/test_readme.py b/tests/test_readme.py new file mode 100644 index 0000000..c2f2092 --- /dev/null +++ b/tests/test_readme.py @@ -0,0 +1,113 @@ +"""Contract tests for the public installation and usage documentation.""" + +from pathlib import Path + + +_REPOSITORY_ROOT = Path(__file__).parents[1] +_README_PATH = _REPOSITORY_ROOT / "README.md" + + +def _readme() -> str: + """Return the public README with insignificant whitespace normalized.""" + + return " ".join( + _README_PATH.read_text(encoding="utf-8").split() + ) + + +def test_readme_documents_cross_platform_installation() -> None: + """Clone, virtual environment, activation, and install steps are public.""" + + readme = _readme() + required_commands = ( + "git clone https://github.com/batuthzcode/securecode-analyzer.git", + "python -m venv .venv", + ".venv\\Scripts\\activate.bat", + ".\\.venv\\Scripts\\Activate.ps1", + "source .venv/bin/activate", + "python -m pip install -e .", + 'python -m pip install -e ".[dev]"', + ) + + assert all(command in readme for command in required_commands) + + +def test_readme_documents_static_analyzer_contract() -> None: + """Static text, JSON, threshold, options, and exits are documented.""" + + readme = _readme() + + assert "securecode-analyzer src" in readme + assert "securecode-analyzer src --format json" in readme + assert "securecode-analyzer src --fail-on warning" in readme + assert "`any`, `info`, `warning` veya `error`" in readme + assert "[WARNING] SA005 src/example.py:1:1" in readme + assert "Hedef, dosya okuma, Unicode" in readme + + +def test_readme_documents_dependency_scanner_contract() -> None: + """Dependency scan formats, options, OSV, output, and exits are public.""" + + readme = _readme() + + assert "securecode-dependency-scan requirements.txt" in readme + assert "--output reports\\local\\dependency-scan.json" in readme + assert "--fail-on high" in readme + assert "--source` | Hayır | `osv`" in readme + assert "--timeout` | Hayır | `10.0`" in readme + assert "https://api.osv.dev/v1/query" in readme + assert "PYSEC-2024-38" in readme + + +def test_readme_distinguishes_live_and_local_osv_data() -> None: + """The deterministic fixture should not be presented as a live database.""" + + readme = _readme() + + assert "python -m tools.run_ci_dependency_scan" in readme + assert "tests/fixtures/osv/fastapi-0.109.0.json" in readme + assert "HTTP isteği göndermez" in readme + assert "genel amaçlı offline vulnerability database değildir" in readme + + +def test_readme_documents_required_limitations() -> None: + """Language, format, false-positive, API, and fix limits are explicit.""" + + readme = _readme() + + assert "yalnızca Python `.py`" in readme + assert "yalnızca `package==version`" in readme + assert "false positive veya false negative" in readme + assert "OSV API erişilebilirliğine" in readme + assert "güvenli sürüm bilgisi `null` kalabilir" in readme + + +def test_readme_references_existing_project_documents() -> None: + """Primary documentation links should resolve inside the repository.""" + + documentation_paths = ( + "docs/README.md", + "docs/scope.md", + "docs/project-plan.md", + "docs/self-analysis.md", + "docs/ci.md", + "docs/components/static-analyzer/README.md", + "docs/components/dependency-scanner/README.md", + "docs/components/sample-web-app/README.md", + ) + + assert all( + (_REPOSITORY_ROOT / path).is_file() + for path in documentation_paths + ) + + +def test_readme_lists_every_static_rule() -> None: + """The public rule table should contain the complete default ruleset.""" + + readme = _readme() + + assert all( + f"`SA{rule_number:03d}`" in readme + for rule_number in range(1, 7) + )