diff --git a/CLAUDE.md b/CLAUDE.md index ded31a30b..74efdc63b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,7 +7,7 @@ This is not aspirational. This is a hard requirement. If you push code that fail ## Thresholds (non-negotiable) -- **Issues on all providers:** 0 (SonarCloud, CodeQL, QLTY, DeepSource, DeepScan, Sentry) +- **Issues on all providers:** 0 (SonarCloud, CodeQL, QLTY, DeepSource, Sentry) - **Coverage:** 100% line AND branch (Codecov, SonarCloud) - **Security alerts:** 0 (Dependabot, CodeQL, Semgrep) - **Suppressions:** FORBIDDEN. No `// NOSONAR`, no `#pragma warning disable`, no `# noqa`, no `[SuppressMessage]`. Fix the code. diff --git a/docs/quality/QUALITY_ZERO_GATES.md b/docs/quality/QUALITY_ZERO_GATES.md index b9afd8ec0..beea4204d 100644 --- a/docs/quality/QUALITY_ZERO_GATES.md +++ b/docs/quality/QUALITY_ZERO_GATES.md @@ -3,7 +3,7 @@ This repository is configured for strict quality enforcement: - Coverage target: 100% (project + patch) -- Mandatory zero-open findings: Sonar, Codacy, Semgrep, Sentry, DeepScan +- Mandatory zero-open findings: Sonar, Codacy, Semgrep, Sentry - Fail-closed secrets preflight - Aggregated required-context assertion via `Quality Zero Gate` diff --git a/ruff.toml b/ruff.toml index 150f4824f..cc21b0905 100644 --- a/ruff.toml +++ b/ruff.toml @@ -38,9 +38,8 @@ ignore = ["E501"] # E402 (module-level import not at top): these SaaS-zero check scripts insert the # repo's helper dir onto sys.path before importing security_helpers — a # deliberate bootstrap pattern. The scripts are slated for removal in the lean- -# gate migration runbook (they back the deleted Sonar/Sentry/Codacy/DeepScan +# gate migration runbook (they back the deleted Sonar/Sentry/Codacy # gates); ignoring E402 here avoids churning soon-to-be-deleted code. "scripts/quality/check_sentry_zero.py" = ["E402"] "scripts/quality/check_sonar_zero.py" = ["E402"] "scripts/quality/check_codacy_zero.py" = ["E402"] -"scripts/quality/check_deepscan_zero.py" = ["E402"] diff --git a/scripts/quality/check_deepscan_zero.py b/scripts/quality/check_deepscan_zero.py deleted file mode 100644 index 71580dcb9..000000000 --- a/scripts/quality/check_deepscan_zero.py +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/env python3 -from __future__ import annotations - -import argparse -import json -import os -import sys -import urllib.error -import urllib.request -from datetime import datetime, timezone -from pathlib import Path -from typing import Any, Dict, List, Optional, Tuple - -_SCRIPT_DIR = Path(__file__).resolve().parent -_HELPER_ROOT = _SCRIPT_DIR if (_SCRIPT_DIR / "security_helpers.py").exists() else _SCRIPT_DIR.parent -if str(_HELPER_ROOT) not in sys.path: - sys.path.insert(0, str(_HELPER_ROOT)) - -from security_helpers import normalize_https_url - -TOTAL_KEYS = {"total", "totalItems", "total_items", "count", "hits", "open_issues"} - - -def _parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Assert DeepScan has zero total open issues.") - parser.add_argument( - "--token", - default="", - help="DeepScan API token (falls back to DEEPSCAN_API_TOKEN env)", - ) - parser.add_argument( - "--out-json", default="deepscan-zero/deepscan.json", help="Output JSON path" - ) - parser.add_argument( - "--out-md", default="deepscan-zero/deepscan.md", help="Output markdown path" - ) - return parser.parse_args() - - -def _find_total_in_dict(payload: dict) -> Optional[int]: - """Search a dict's own keys for a recognized total key with a numeric value.""" - for key, value in payload.items(): - if key in TOTAL_KEYS and isinstance(value, (int, float)): - return int(value) - return None - - -def _find_total_in_children(children: Any) -> Optional[int]: - """Recursively search an iterable of child values for a total count.""" - for child in children: - total = extract_total_open(child) - if total is not None: - return total - return None - - -def extract_total_open(payload: Any) -> Optional[int]: - """Walk a JSON payload tree to find the first recognized total-count field.""" - if isinstance(payload, dict): - direct = _find_total_in_dict(payload) - if direct is not None: - return direct - return _find_total_in_children(payload.values()) - if isinstance(payload, list): - return _find_total_in_children(payload) - return None - - -def _request_json(url: str, token: str) -> Dict[str, Any]: - safe_url = normalize_https_url(url, allowed_host_suffixes={"deepscan.io"}) - req = urllib.request.Request( - safe_url, - headers={ - "Accept": "application/json", - "Authorization": f"Bearer {token}", - "User-Agent": "reframe-deepscan-zero-gate", - }, - method="GET", - ) - # URL validated above via normalize_https_url - with urllib.request.urlopen(req, timeout=30) as resp: - return json.loads(resp.read().decode("utf-8")) - - -def _render_md(payload: dict) -> str: - lines = [ - "# DeepScan Zero Gate", - "", - f"- Status: `{payload['status']}`", - f"- Open issues: `{payload.get('open_issues')}`", - f"- Source URL: `{payload.get('open_issues_url') or 'n/a'}`", - f"- Timestamp (UTC): `{payload['timestamp_utc']}`", - "", - "## Findings", - ] - findings = payload.get("findings") or [] - if findings: - lines.extend(f"- {item}" for item in findings) - else: - lines.append("- None") - return "\n".join(lines) + "\n" - - -def _safe_output_path(raw: str, fallback: str, base: Optional[Path] = None) -> Path: - root = (base or Path.cwd()).resolve() - candidate = Path((raw or "").strip() or fallback).expanduser() - if not candidate.is_absolute(): - candidate = root / candidate - resolved = candidate.resolve(strict=False) - try: - resolved.relative_to(root) - except ValueError as exc: - raise ValueError(f"Output path escapes workspace root: {candidate}") from exc - return resolved - - -def _validate_config(token: str, open_issues_url: str) -> Tuple[str, List[str]]: - """Validate token and URL configuration, returning the normalized URL and any findings.""" - findings: List[str] = [] - if not token: - findings.append("DEEPSCAN_API_TOKEN is missing.") - if not open_issues_url: - findings.append("DEEPSCAN_OPEN_ISSUES_URL is missing.") - else: - try: - open_issues_url = normalize_https_url( - open_issues_url, - allowed_host_suffixes={"deepscan.io"}, - ) - except ValueError as exc: - findings.append(str(exc)) - return open_issues_url, findings - - -def _query_and_evaluate(open_issues_url: str, token: str) -> Tuple[str, Optional[int], List[str]]: - """Query DeepScan API and evaluate the result against the zero-issue threshold.""" - findings: List[str] = [] - open_issues: Optional[int] = None - try: - payload = _request_json(open_issues_url, token) - open_issues = extract_total_open(payload) - if open_issues is None: - findings.append("DeepScan response did not include a parseable total issue count.") - elif open_issues != 0: - findings.append(f"DeepScan reports {open_issues} open issues (expected 0).") - status = "pass" if not findings else "fail" - except (OSError, ValueError) as exc: # pragma: no cover - network/runtime surface - findings.append(f"DeepScan API request failed: {exc}") - status = "fail" - return status, open_issues, findings - - -def _write_reports(payload: dict, out_json: Path, out_md: Path) -> None: - """Write the JSON and markdown report files and print the markdown to stdout.""" - out_json.parent.mkdir(parents=True, exist_ok=True) - out_md.parent.mkdir(parents=True, exist_ok=True) - out_json.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8") - out_md.write_text(_render_md(payload), encoding="utf-8") - print(out_md.read_text(encoding="utf-8"), end="") - - -def main() -> int: - args = _parse_args() - token = (args.token or os.environ.get("DEEPSCAN_API_TOKEN", "")).strip() - raw_url = os.environ.get("DEEPSCAN_OPEN_ISSUES_URL", "").strip() - - open_issues_url, findings = _validate_config(token, raw_url) - open_issues: Optional[int] = None - - status = "fail" - if not findings: - status, open_issues, api_findings = _query_and_evaluate(open_issues_url, token) - findings.extend(api_findings) - - payload = { - "status": status, - "open_issues": open_issues, - "open_issues_url": open_issues_url, - "timestamp_utc": datetime.now(timezone.utc).isoformat(), - "findings": findings, - } - - try: - out_json = _safe_output_path(args.out_json, "deepscan-zero/deepscan.json") - out_md = _safe_output_path(args.out_md, "deepscan-zero/deepscan.md") - except ValueError as exc: - print(str(exc), file=sys.stderr) - return 1 - - _write_reports(payload, out_json, out_md) - return 0 if status == "pass" else 1 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/tests/python/test_check_deepscan_zero.py b/tests/python/test_check_deepscan_zero.py deleted file mode 100644 index aa145eddf..000000000 --- a/tests/python/test_check_deepscan_zero.py +++ /dev/null @@ -1,118 +0,0 @@ -"""Tests for scripts/quality/check_deepscan_zero.py.""" - -from __future__ import annotations - -from pathlib import Path - -from conftest import load_script_module - -mod = load_script_module("scripts/quality/check_deepscan_zero.py", "check_deepscan_zero") - - -def test_extract_total_open_dict_direct() -> None: - assert mod.extract_total_open({"count": 7}) == 7 - - -def test_extract_total_open_nested_dict() -> None: - assert mod.extract_total_open({"a": {"b": {"total": 0}}}) == 0 - - -def test_extract_total_open_list() -> None: - assert mod.extract_total_open([{"x": 1}, {"hits": 3}]) == 3 - - -def test_extract_total_open_none() -> None: - assert mod.extract_total_open("string") is None - assert mod.extract_total_open({"k": "v"}) is None - - -def test_find_total_in_dict_non_numeric() -> None: - assert mod._find_total_in_dict({"total": "x"}) is None - - -def test_render_md_with_findings() -> None: - md = mod._render_md( - { - "status": "fail", - "open_issues": 1, - "open_issues_url": "https://x.deepscan.io", - "timestamp_utc": "t", - "findings": ["f"], - } - ) - assert "deepscan.io" in md and "- f" in md - - -def test_render_md_no_url_no_findings() -> None: - md = mod._render_md( - {"status": "pass", "open_issues": 0, "open_issues_url": "", "timestamp_utc": "t"} - ) - assert "n/a" in md and "- None" in md - - -def test_validate_config_missing_token_and_url() -> None: - url, findings = mod._validate_config("", "") - assert any("DEEPSCAN_API_TOKEN" in f for f in findings) - assert any("DEEPSCAN_OPEN_ISSUES_URL" in f for f in findings) - - -def test_validate_config_bad_url() -> None: - url, findings = mod._validate_config("tok", "http://insecure.deepscan.io") - assert any("https" in f for f in findings) - - -def test_validate_config_ok() -> None: - url, findings = mod._validate_config("tok", "https://api.deepscan.io/issues") - assert findings == [] - assert url.startswith("https://") - - -def test_query_and_evaluate_pass(monkeypatch) -> None: - monkeypatch.setattr(mod, "_request_json", lambda *a, **k: {"total": 0}) - status, issues, findings = mod._query_and_evaluate("https://api.deepscan.io", "tok") - assert status == "pass" and issues == 0 - - -def test_query_and_evaluate_nonzero(monkeypatch) -> None: - monkeypatch.setattr(mod, "_request_json", lambda *a, **k: {"total": 9}) - status, issues, findings = mod._query_and_evaluate("https://api.deepscan.io", "tok") - assert status == "fail" and issues == 9 - - -def test_query_and_evaluate_unparseable(monkeypatch) -> None: - monkeypatch.setattr(mod, "_request_json", lambda *a, **k: {"nope": 1}) - status, issues, findings = mod._query_and_evaluate("https://api.deepscan.io", "tok") - assert status == "fail" and issues is None - - -def test_safe_output_path_absolute_inside_root(tmp_path: Path) -> None: - abs_target = tmp_path / "sub" / "a.json" - out = mod._safe_output_path(str(abs_target), "fb", base=tmp_path) - assert out == abs_target.resolve() - - -def test_main_config_error(tmp_path: Path, monkeypatch) -> None: - monkeypatch.delenv("DEEPSCAN_API_TOKEN", raising=False) - monkeypatch.delenv("DEEPSCAN_OPEN_ISSUES_URL", raising=False) - monkeypatch.chdir(tmp_path) - monkeypatch.setattr("sys.argv", ["check_deepscan_zero.py"]) - assert mod.main() == 1 - - -def test_main_pass(tmp_path: Path, monkeypatch) -> None: - monkeypatch.setenv("DEEPSCAN_API_TOKEN", "tok") - monkeypatch.setenv("DEEPSCAN_OPEN_ISSUES_URL", "https://api.deepscan.io/issues") - monkeypatch.setattr(mod, "_request_json", lambda *a, **k: {"total": 0}) - monkeypatch.chdir(tmp_path) - monkeypatch.setattr("sys.argv", ["check_deepscan_zero.py"]) - assert mod.main() == 0 - - -def test_main_bad_output(tmp_path: Path, monkeypatch, capsys) -> None: - monkeypatch.setenv("DEEPSCAN_API_TOKEN", "tok") - monkeypatch.setenv("DEEPSCAN_OPEN_ISSUES_URL", "https://api.deepscan.io/issues") - monkeypatch.setattr(mod, "_request_json", lambda *a, **k: {"total": 0}) - monkeypatch.chdir(tmp_path) - monkeypatch.setattr("sys.argv", ["check_deepscan_zero.py", "--out-json", "../bad.json"]) - assert mod.main() == 1 - assert "escapes workspace root" in capsys.readouterr().err diff --git a/tests/python/test_helper_bootstrap.py b/tests/python/test_helper_bootstrap.py index 7a84cf244..6fee571a2 100644 --- a/tests/python/test_helper_bootstrap.py +++ b/tests/python/test_helper_bootstrap.py @@ -20,7 +20,6 @@ MODULES = [ "scripts/quality/check_codacy_zero.py", - "scripts/quality/check_deepscan_zero.py", "scripts/quality/check_sentry_zero.py", "scripts/quality/check_sonar_zero.py", ] diff --git a/tests/python/test_network_request_helpers.py b/tests/python/test_network_request_helpers.py index 0bce4bca2..63848abf0 100644 --- a/tests/python/test_network_request_helpers.py +++ b/tests/python/test_network_request_helpers.py @@ -13,7 +13,6 @@ from conftest import load_script_module codacy = load_script_module("scripts/quality/check_codacy_zero.py", "check_codacy_zero_net") -deepscan = load_script_module("scripts/quality/check_deepscan_zero.py", "check_deepscan_zero_net") sentry = load_script_module("scripts/quality/check_sentry_zero.py", "check_sentry_zero_net") sonar = load_script_module("scripts/quality/check_sonar_zero.py", "check_sonar_zero_net") required = load_script_module( @@ -60,11 +59,6 @@ def test_codacy_request_json_post_with_data(monkeypatch) -> None: assert captured[0].data is not None -def test_deepscan_request_json(monkeypatch) -> None: - _patch_urlopen(monkeypatch, deepscan, _Resp(json.dumps({"count": 0}).encode())) - assert deepscan._request_json("https://api.deepscan.io/x", "tok") == {"count": 0} - - def test_sonar_request_json(monkeypatch) -> None: _patch_urlopen(monkeypatch, sonar, _Resp(json.dumps({"paging": {"total": 0}}).encode())) assert sonar._request_json("https://sonarcloud.io/api/x", "auth") == {"paging": {"total": 0}} diff --git a/tools/quality/assert-deepscan-zero-backlog.ps1 b/tools/quality/assert-deepscan-zero-backlog.ps1 deleted file mode 100644 index 0c523f856..000000000 --- a/tools/quality/assert-deepscan-zero-backlog.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -param( - [string]$ConfigPath = "tools/quality/provider-gate.config.json", - [switch]$Strict -) - -$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path -$helperPath = Join-Path $scriptDir "invoke-provider-zero-gate.ps1" -if (-not (Test-Path -Path $helperPath)) { - throw "Provider gate helper not found: $helperPath" -} - -$providerKey = "deepscan" -& $helperPath -ProviderKey $providerKey -ConfigPath $ConfigPath -Strict:$Strict diff --git a/tools/quality/provider-gate.config.json b/tools/quality/provider-gate.config.json index 891b81ff6..e691184d8 100644 --- a/tools/quality/provider-gate.config.json +++ b/tools/quality/provider-gate.config.json @@ -36,16 +36,6 @@ ], "countJsonPath": "total" }, - "deepscan": { - "method": "GET", - "url": "${DEEPSCAN_ZERO_BACKLOG_URL}", - "authScheme": "", - "tokenEnv": "", - "requiredEnv": [ - "DEEPSCAN_ZERO_BACKLOG_URL" - ], - "countJsonPath": "data.lastUnresolvedIssueCount" - }, "applitools": { "method": "GET", "url": "https://eyesapi.applitools.com/api/sessions/batches",