shadowcat 2.2.0__tar.gz → 2.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {shadowcat-2.2.0 → shadowcat-2.2.1}/PKG-INFO +1 -1
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/tools/general_report_tool.py +27 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/pyproject.toml +1 -1
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_general_report_tool.py +31 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/uv.lock +1 -1
- {shadowcat-2.2.0 → shadowcat-2.2.1}/.clineignore +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/.env.example +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/.gitignore +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/.gitmodules +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/.mcp.json +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/CLAUDE.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/Dockerfile +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/LICENSE.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/Makefile +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/README.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/benchmark/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/benchmark/cli.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/benchmark/config.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/benchmark/docker.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/benchmark/registry.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/agent.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/backend.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/config.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/controller.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/events.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/langfuse.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/phantom.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/planner.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/profiling.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/sanitizer.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/session.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/core/tracer.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/interface/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/interface/components/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/interface/components/activity_feed.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/interface/components/renderers.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/interface/components/splash.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/interface/main.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/interface/styles.tcss +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/interface/tui.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/parsing/html_distiller.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/parsing/tool_parser.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/prompts/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/prompts/pentesting.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/rag_module/knowledge_base.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/tests/benchmark_phantom.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/tools/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/tools/base.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/tools/executor.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/agent/tools/registry.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/README.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/api/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/api/routes_scan.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/authz.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/cli.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/compliance/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/compliance/pdpa_mapping.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/core/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/core/coverage.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/core/events.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/core/llm_client.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/core/orchestrator.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/core/scan_memory.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/crawler/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/crawler/csrf.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/crawler/headless.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/crawler/js_analyzer.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/crawler/spider.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/crawler/subdomains.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/daemon.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/db/README.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/db/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/db/engine.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/db/models.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/db/repository.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/db/schema.sql +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/modes/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/modes/base.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/modes/ctf.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/modes/enterprise.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/modes/general.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/modes/registry.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/reporting/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/reporting/generator.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/schemas/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/schemas/api.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/tools/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/tools/dirbrute_tool.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/tools/http_tool.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/tools/nuclei_tool.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/tools/report_tool.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/tools/shell_tools.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/user_config.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/verification/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/verification/evidence_store.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/verification/general_oracle.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/verification/idor_oracle.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/waf/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/waf/detector.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/waf/evasion.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/webui/index.html +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/backend/workspace.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/custom_exploits/example_exploit.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docker-compose.yml +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/CHANGES_SUMMARY_TH.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/DASHBOARD_IMPROVEMENTS_TH.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/ENTERPRISE_ARCH.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/HOW_TO_RUN_TH.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/NSC_PREP_TH.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/PROGRESS.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/ROADMAP.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/ShadowCat_/340/271/200/340/270/255/340/270/201/340/270/252/340/270/262/340/270/243/340/270/233/340/270/243/340/270/260/340/270/201/340/270/255/340/270/232/340/271/202/340/270/233/340/270/243/340/271/200/340/270/210/340/270/204_NSC2026.pdf" +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/TECHNICAL_DEEP_DIVE_TH.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/WEBSITE_SPEC.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/nsc-shadowcat-pitch.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/docs/research/README.md +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/fix-workspace-permissions.sh +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/next.config.ts +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/postcss.config.mjs +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/public/logo-mark.png +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/public/logo.png +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/app/globals.css +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/app/layout.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/app/page.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/AppShell.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/EmptyBlock.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/NewScanModal.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/PageHeader.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/ReportButtons.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/Sidebar.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/StatusChip.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/Topbar.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/charts/SeverityBarChart.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/charts/VerdictBar.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/icons.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/views/FindingsView.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/views/LiveFeedView.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/views/MemoryView.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/views/OverviewView.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/views/SessionsView.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/components/views/SettingsView.tsx +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/lib/api.ts +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/lib/severity.ts +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/lib/useActiveScan.ts +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/src/lib/useScanDetails.ts +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/frontend/tsconfig.json +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/hatch_build.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/scripts/config.sh +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/scripts/entrypoint.sh +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/scripts/gen_report.js +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/scripts/test_client.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/setup.sh +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/conftest.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/docker/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/docker/test_container_health.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/docker/test_docker_build.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/integration/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/integration/test_benchmark_cli.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/integration/test_controller.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_config_cli.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_coverage_ledger.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_daemon_cli.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_db_persistence.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_enterprise_api.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_enterprise_engine.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_executor.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_general_oracle.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_headless.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_llm_wiring.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_memory_briefing.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_pdpa_mapping.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_scan_memory.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_sse_replay.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/test_user_config.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/unit/__init__.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/unit/test_backend_interface.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/unit/test_benchmark_registry.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/unit/test_config.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/unit/test_events.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/unit/test_flag_detection.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/unit/test_langfuse.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/unit/test_phantom_two_tier.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/unit/test_planner_context.py +0 -0
- {shadowcat-2.2.0 → shadowcat-2.2.1}/tests/unit/test_session.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shadowcat
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.1
|
|
4
4
|
Summary: AI-powered autonomous web security scanner with real-time streaming — enterprise DAST platform
|
|
5
5
|
Project-URL: Homepage, https://github.com/shadowcat-dast/shadowcat
|
|
6
6
|
Project-URL: Repository, https://github.com/shadowcat-dast/shadowcat
|
|
@@ -15,6 +15,19 @@ from pydantic import ValidationError
|
|
|
15
15
|
from backend.schemas.api import GeneralFindingReport
|
|
16
16
|
from backend.verification.evidence_store import EvidenceStore
|
|
17
17
|
|
|
18
|
+
# CWE classes where GeneralOracle's verdict is structurally incapable of
|
|
19
|
+
# rising above "inconclusive" without a baseline (see general_oracle.py's
|
|
20
|
+
# CWE-639/284 branch: no baseline -> no way to prove the response is even
|
|
21
|
+
# unusual, let alone another user's data). Rejecting these at submission time
|
|
22
|
+
# means the agent never wastes a report on a hypothesis whose ceiling is known
|
|
23
|
+
# in advance — the sufficiency gate is enforced here, not left as a "the
|
|
24
|
+
# oracle will just say inconclusive" dead end downstream.
|
|
25
|
+
_IDOR_REQUIRES_BASELINE = {"639", "284"}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _cwe_digits(cwe: str) -> str:
|
|
29
|
+
return "".join(ch for ch in (cwe or "") if ch.isdigit())
|
|
30
|
+
|
|
18
31
|
|
|
19
32
|
class ReportGeneralFindingTool:
|
|
20
33
|
"""Validate, ground, and record a General DAST candidate finding."""
|
|
@@ -107,6 +120,20 @@ class ReportGeneralFindingTool:
|
|
|
107
120
|
"note": "Fix the fields and resubmit. You cannot add fields or declare a verdict.",
|
|
108
121
|
}
|
|
109
122
|
|
|
123
|
+
if _cwe_digits(report.vuln_class.cwe) in _IDOR_REQUIRES_BASELINE and not report.evidence.baseline:
|
|
124
|
+
return {
|
|
125
|
+
"error": "missing_baseline",
|
|
126
|
+
"note": (
|
|
127
|
+
"CWE-639/284 (IDOR / broken object-level access) can only ever be "
|
|
128
|
+
"verified against a baseline showing normal, authorized access to "
|
|
129
|
+
"compare with — without one the oracle can never return more than "
|
|
130
|
+
"'inconclusive', so don't file it yet. Either capture a baseline "
|
|
131
|
+
"request_id first, or if you're only testing 'does this endpoint "
|
|
132
|
+
"require authentication at all' (no comparison needed), reclassify "
|
|
133
|
+
"as CWE-287 or CWE-306 instead — that class doesn't need a baseline."
|
|
134
|
+
),
|
|
135
|
+
}
|
|
136
|
+
|
|
110
137
|
cited = report.cited_request_ids()
|
|
111
138
|
missing = [rid for rid in cited if not self._evidence.has(rid)]
|
|
112
139
|
if missing:
|
|
@@ -45,6 +45,37 @@ async def test_report_finding_rejects_duplicate_signal() -> None:
|
|
|
45
45
|
assert len(tool.findings) == 1 # the duplicate was never recorded
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
async def test_report_finding_rejects_idor_without_baseline() -> None:
|
|
49
|
+
"""CWE-639/284 without a baseline is guaranteed 'inconclusive' downstream —
|
|
50
|
+
reject at submission so the agent doesn't waste a report on a known dead end."""
|
|
51
|
+
store = EvidenceStore("s")
|
|
52
|
+
rid = store.put({"method": "GET", "url": "https://t/rest/order-history"}, {"status": 200})
|
|
53
|
+
tool = ReportGeneralFindingTool(store)
|
|
54
|
+
result = await tool.run(
|
|
55
|
+
title="Possible IDOR on /rest/order-history",
|
|
56
|
+
hypothesis="h",
|
|
57
|
+
vuln_class={"cwe": "CWE-639"},
|
|
58
|
+
evidence={"probe": rid},
|
|
59
|
+
)
|
|
60
|
+
assert result["error"] == "missing_baseline"
|
|
61
|
+
assert tool.findings == []
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
async def test_report_finding_accepts_idor_with_baseline() -> None:
|
|
65
|
+
store = EvidenceStore("s")
|
|
66
|
+
base = store.put({"method": "GET", "url": "https://t/rest/order-history/1"}, {"status": 200})
|
|
67
|
+
probe = store.put({"method": "GET", "url": "https://t/rest/order-history/2"}, {"status": 200})
|
|
68
|
+
tool = ReportGeneralFindingTool(store)
|
|
69
|
+
result = await tool.run(
|
|
70
|
+
title="IDOR on /rest/order-history",
|
|
71
|
+
hypothesis="h",
|
|
72
|
+
vuln_class={"cwe": "CWE-639"},
|
|
73
|
+
evidence={"probe": probe, "baseline": base},
|
|
74
|
+
)
|
|
75
|
+
assert "error" not in result
|
|
76
|
+
assert len(tool.findings) == 1
|
|
77
|
+
|
|
78
|
+
|
|
48
79
|
async def test_report_finding_allows_same_probe_for_a_different_cwe() -> None:
|
|
49
80
|
"""The same request_id may legitimately support two different findings."""
|
|
50
81
|
store = EvidenceStore("s")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|