codesecure-core 1.0.0b10__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.
- codesecure_core-1.0.0b10/LICENSE +21 -0
- codesecure_core-1.0.0b10/PKG-INFO +101 -0
- codesecure_core-1.0.0b10/README.md +69 -0
- codesecure_core-1.0.0b10/pyproject.toml +40 -0
- codesecure_core-1.0.0b10/setup.cfg +4 -0
- codesecure_core-1.0.0b10/setup.py +5 -0
- codesecure_core-1.0.0b10/src/codesecure/__init__.py +19 -0
- codesecure_core-1.0.0b10/src/codesecure/ai_providers/__init__.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure/ai_providers/anthropic_provider.py +144 -0
- codesecure_core-1.0.0b10/src/codesecure/ai_providers/azure_cli.py +428 -0
- codesecure_core-1.0.0b10/src/codesecure/ai_providers/base.py +89 -0
- codesecure_core-1.0.0b10/src/codesecure/ai_providers/google_cli.py +195 -0
- codesecure_core-1.0.0b10/src/codesecure/ai_providers/guides.py +51 -0
- codesecure_core-1.0.0b10/src/codesecure/ai_providers/kiro_cli.py +421 -0
- codesecure_core-1.0.0b10/src/codesecure/ai_providers/manager.py +525 -0
- codesecure_core-1.0.0b10/src/codesecure/ai_providers/openai_provider.py +143 -0
- codesecure_core-1.0.0b10/src/codesecure/ai_providers/prompts.py +276 -0
- codesecure_core-1.0.0b10/src/codesecure/common/__init__.py +64 -0
- codesecure_core-1.0.0b10/src/codesecure/common/cloud_provider.py +208 -0
- codesecure_core-1.0.0b10/src/codesecure/common/config.py +78 -0
- codesecure_core-1.0.0b10/src/codesecure/common/config_manager.py +80 -0
- codesecure_core-1.0.0b10/src/codesecure/common/logging.py +205 -0
- codesecure_core-1.0.0b10/src/codesecure/common/models.py +247 -0
- codesecure_core-1.0.0b10/src/codesecure/common/performance.py +111 -0
- codesecure_core-1.0.0b10/src/codesecure/jobs/__init__.py +18 -0
- codesecure_core-1.0.0b10/src/codesecure/jobs/manager.py +421 -0
- codesecure_core-1.0.0b10/src/codesecure/reports/__init__.py +32 -0
- codesecure_core-1.0.0b10/src/codesecure/reports/generator.py +202 -0
- codesecure_core-1.0.0b10/src/codesecure/reports/html.py +766 -0
- codesecure_core-1.0.0b10/src/codesecure/reports/json.py +148 -0
- codesecure_core-1.0.0b10/src/codesecure/reports/markdown.py +293 -0
- codesecure_core-1.0.0b10/src/codesecure/reports/sarif.py +229 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/__init__.py +61 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/bandit.py +165 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/base.py +344 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/checkov.py +198 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/data_merger.py +270 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/deduplication_engine.py +282 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/detect_secrets.py +109 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/engine.py +351 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/framework_mapper.py +158 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/grype.py +124 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/npm_audit.py +134 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/pip_audit.py +149 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/pip_licenses.py +130 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/semgrep.py +123 -0
- codesecure_core-1.0.0b10/src/codesecure/scanners/syft.py +111 -0
- codesecure_core-1.0.0b10/src/codesecure/security/__init__.py +45 -0
- codesecure_core-1.0.0b10/src/codesecure/security/output_sanitizer.py +202 -0
- codesecure_core-1.0.0b10/src/codesecure/security/security_validator.py +300 -0
- codesecure_core-1.0.0b10/src/codesecure/security/subprocess_runner.py +366 -0
- codesecure_core-1.0.0b10/src/codesecure/security/validators.py +300 -0
- codesecure_core-1.0.0b10/src/codesecure/security_matrix/__init__.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure/security_matrix/file_filter.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure/security_matrix/neolifter.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure/security_validate/__init__.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure/security_validate/cdk.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure/security_validate/cloudformation.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure/security_validate/severity.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure/security_validate/terraform.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure/telemetry/__init__.py +3 -0
- codesecure_core-1.0.0b10/src/codesecure/telemetry/client.py +90 -0
- codesecure_core-1.0.0b10/src/codesecure/threat_model/__init__.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure/threat_model/action_plan.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure/threat_model/stride_engine.py +0 -0
- codesecure_core-1.0.0b10/src/codesecure_core.egg-info/PKG-INFO +101 -0
- codesecure_core-1.0.0b10/src/codesecure_core.egg-info/SOURCES.txt +69 -0
- codesecure_core-1.0.0b10/src/codesecure_core.egg-info/dependency_links.txt +1 -0
- codesecure_core-1.0.0b10/src/codesecure_core.egg-info/requires.txt +28 -0
- codesecure_core-1.0.0b10/src/codesecure_core.egg-info/top_level.txt +1 -0
- codesecure_core-1.0.0b10/tests/test_openai_provider.py +82 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 noviqtechnologies
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codesecure-core
|
|
3
|
+
Version: 1.0.0b10
|
|
4
|
+
Summary: Enterprise-grade security analysis core engine
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: pydantic>=2.0.0
|
|
10
|
+
Requires-Dist: jinja2>=3.1.0
|
|
11
|
+
Requires-Dist: asyncio>=3.4.3
|
|
12
|
+
Requires-Dist: rich>=13.0.0
|
|
13
|
+
Requires-Dist: mermaid-py>=0.1.0
|
|
14
|
+
Requires-Dist: markdown>=3.5.2
|
|
15
|
+
Requires-Dist: pygments>=2.17.2
|
|
16
|
+
Requires-Dist: packaging>=24.0
|
|
17
|
+
Requires-Dist: bandit>=1.7.0
|
|
18
|
+
Requires-Dist: semgrep>=1.0.0; sys_platform != "win32"
|
|
19
|
+
Requires-Dist: checkov>=3.0.0
|
|
20
|
+
Requires-Dist: detect-secrets>=1.4.0
|
|
21
|
+
Requires-Dist: pip-audit>=2.0.0
|
|
22
|
+
Requires-Dist: pip-licenses>=4.0.0
|
|
23
|
+
Requires-Dist: pyyaml>=6.0
|
|
24
|
+
Provides-Extra: google
|
|
25
|
+
Requires-Dist: google-generativeai>=0.3.0; extra == "google"
|
|
26
|
+
Provides-Extra: openai
|
|
27
|
+
Requires-Dist: openai>=1.0.0; extra == "openai"
|
|
28
|
+
Provides-Extra: aws
|
|
29
|
+
Provides-Extra: all
|
|
30
|
+
Requires-Dist: codesecure-core[aws,google,openai]; extra == "all"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# CodeSecure Core (`codesecure-core`)
|
|
34
|
+
|
|
35
|
+
The `codesecure-core` package is the programmatic orchestration brain of the CodeSecure platform. It provides the centralized, stateless logic for executing security scanners, managing asynchronous jobs, and enriching findings with AI models.
|
|
36
|
+
|
|
37
|
+
## 🎯 Module Purpose
|
|
38
|
+
|
|
39
|
+
This package encapsulates the strict business logic of the platform, adhering to a "Thin Client" architecture. It does not export command-line (CLI) applications or MCP Transport interfaces directly. Instead, it provides a stable Python API (Singletons) designed to be consumed by other packages in the CodeSecure monorepo, such as `codesecure-cli` and `codesecure-mcp`.
|
|
40
|
+
|
|
41
|
+
## 📦 Local Installation
|
|
42
|
+
|
|
43
|
+
Because `core` has no dependency on the UI/CLI layer, it can be installed natively for programmatic API usage.
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
cd packages/core
|
|
47
|
+
python -m venv .venv
|
|
48
|
+
|
|
49
|
+
# Install the core logic with basic SAST scanners
|
|
50
|
+
pip install -e .
|
|
51
|
+
|
|
52
|
+
# [Optional] Install AI providers (Google Gemini or Kiro CLI dependencies)
|
|
53
|
+
pip install -e .[google,aws]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 🔌 Exported APIs & Features
|
|
57
|
+
|
|
58
|
+
The Core package exposes Manager classes via the Singleton pattern:
|
|
59
|
+
|
|
60
|
+
1. **`ScannerEngine`**: Orchestrates local/container execution for Bandit, Semgrep, Checkov, detect-secrets, npm-audit, pip-audit, etc.
|
|
61
|
+
```python
|
|
62
|
+
from codesecure.scanners.engine import get_scanner_engine
|
|
63
|
+
```
|
|
64
|
+
2. **`JobManager`**: Async execution tracking, lock management, TTL limits, and progress percentages.
|
|
65
|
+
```python
|
|
66
|
+
from codesecure.jobs.manager import get_job_manager
|
|
67
|
+
```
|
|
68
|
+
3. **`AIProviderManager`**: Abstracts batch prompting against Gemini and Kiro. Calculates False Positive tracking dynamically.
|
|
69
|
+
```python
|
|
70
|
+
from codesecure.ai_providers.manager import get_ai_manager
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 🛠️ Integration Example
|
|
74
|
+
|
|
75
|
+
Here is how a downstream module (like the MCP server) imports and utilizes the core library programmatically:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import asyncio
|
|
79
|
+
from pathlib import Path
|
|
80
|
+
from codesecure.common.models import ScanMode, CloudProvider
|
|
81
|
+
from codesecure.scanners.engine import get_scanner_engine
|
|
82
|
+
|
|
83
|
+
async def programmatically_scan(target_dir: str):
|
|
84
|
+
scan_path = Path(target_dir).resolve()
|
|
85
|
+
engine = get_scanner_engine()
|
|
86
|
+
|
|
87
|
+
# Check available scanners
|
|
88
|
+
available = engine.get_available_scanners(ScanMode.LOCAL)
|
|
89
|
+
print(f"Scanners ready: {available}")
|
|
90
|
+
|
|
91
|
+
# Run a unified scan seamlessly combining multiple tools
|
|
92
|
+
result = await engine.run_scan(
|
|
93
|
+
path=scan_path,
|
|
94
|
+
mode=ScanMode.LOCAL,
|
|
95
|
+
cloud_provider=CloudProvider.NONE
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
print(f"Total findings discovered: {len(result.findings)}")
|
|
99
|
+
|
|
100
|
+
asyncio.run(programmatically_scan("./my_project"))
|
|
101
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# CodeSecure Core (`codesecure-core`)
|
|
2
|
+
|
|
3
|
+
The `codesecure-core` package is the programmatic orchestration brain of the CodeSecure platform. It provides the centralized, stateless logic for executing security scanners, managing asynchronous jobs, and enriching findings with AI models.
|
|
4
|
+
|
|
5
|
+
## 🎯 Module Purpose
|
|
6
|
+
|
|
7
|
+
This package encapsulates the strict business logic of the platform, adhering to a "Thin Client" architecture. It does not export command-line (CLI) applications or MCP Transport interfaces directly. Instead, it provides a stable Python API (Singletons) designed to be consumed by other packages in the CodeSecure monorepo, such as `codesecure-cli` and `codesecure-mcp`.
|
|
8
|
+
|
|
9
|
+
## 📦 Local Installation
|
|
10
|
+
|
|
11
|
+
Because `core` has no dependency on the UI/CLI layer, it can be installed natively for programmatic API usage.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
cd packages/core
|
|
15
|
+
python -m venv .venv
|
|
16
|
+
|
|
17
|
+
# Install the core logic with basic SAST scanners
|
|
18
|
+
pip install -e .
|
|
19
|
+
|
|
20
|
+
# [Optional] Install AI providers (Google Gemini or Kiro CLI dependencies)
|
|
21
|
+
pip install -e .[google,aws]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 🔌 Exported APIs & Features
|
|
25
|
+
|
|
26
|
+
The Core package exposes Manager classes via the Singleton pattern:
|
|
27
|
+
|
|
28
|
+
1. **`ScannerEngine`**: Orchestrates local/container execution for Bandit, Semgrep, Checkov, detect-secrets, npm-audit, pip-audit, etc.
|
|
29
|
+
```python
|
|
30
|
+
from codesecure.scanners.engine import get_scanner_engine
|
|
31
|
+
```
|
|
32
|
+
2. **`JobManager`**: Async execution tracking, lock management, TTL limits, and progress percentages.
|
|
33
|
+
```python
|
|
34
|
+
from codesecure.jobs.manager import get_job_manager
|
|
35
|
+
```
|
|
36
|
+
3. **`AIProviderManager`**: Abstracts batch prompting against Gemini and Kiro. Calculates False Positive tracking dynamically.
|
|
37
|
+
```python
|
|
38
|
+
from codesecure.ai_providers.manager import get_ai_manager
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 🛠️ Integration Example
|
|
42
|
+
|
|
43
|
+
Here is how a downstream module (like the MCP server) imports and utilizes the core library programmatically:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import asyncio
|
|
47
|
+
from pathlib import Path
|
|
48
|
+
from codesecure.common.models import ScanMode, CloudProvider
|
|
49
|
+
from codesecure.scanners.engine import get_scanner_engine
|
|
50
|
+
|
|
51
|
+
async def programmatically_scan(target_dir: str):
|
|
52
|
+
scan_path = Path(target_dir).resolve()
|
|
53
|
+
engine = get_scanner_engine()
|
|
54
|
+
|
|
55
|
+
# Check available scanners
|
|
56
|
+
available = engine.get_available_scanners(ScanMode.LOCAL)
|
|
57
|
+
print(f"Scanners ready: {available}")
|
|
58
|
+
|
|
59
|
+
# Run a unified scan seamlessly combining multiple tools
|
|
60
|
+
result = await engine.run_scan(
|
|
61
|
+
path=scan_path,
|
|
62
|
+
mode=ScanMode.LOCAL,
|
|
63
|
+
cloud_provider=CloudProvider.NONE
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
print(f"Total findings discovered: {len(result.findings)}")
|
|
67
|
+
|
|
68
|
+
asyncio.run(programmatically_scan("./my_project"))
|
|
69
|
+
```
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "codesecure-core"
|
|
7
|
+
version = "1.0.0b10"
|
|
8
|
+
description = "Enterprise-grade security analysis core engine"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
dependencies = [
|
|
13
|
+
"pydantic>=2.0.0",
|
|
14
|
+
"jinja2>=3.1.0",
|
|
15
|
+
"asyncio>=3.4.3",
|
|
16
|
+
"rich>=13.0.0",
|
|
17
|
+
"mermaid-py>=0.1.0",
|
|
18
|
+
"markdown>=3.5.2",
|
|
19
|
+
"pygments>=2.17.2",
|
|
20
|
+
"packaging>=24.0",
|
|
21
|
+
# Scanners
|
|
22
|
+
"bandit>=1.7.0",
|
|
23
|
+
"semgrep>=1.0.0; sys_platform != 'win32'",
|
|
24
|
+
"checkov>=3.0.0",
|
|
25
|
+
"detect-secrets>=1.4.0",
|
|
26
|
+
"pip-audit>=2.0.0",
|
|
27
|
+
"pip-licenses>=4.0.0",
|
|
28
|
+
"pyyaml>=6.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
google = ["google-generativeai>=0.3.0"]
|
|
33
|
+
openai = ["openai>=1.0.0"]
|
|
34
|
+
# aws = ["kiro-cli>=1.0.0"] # Dependency not found on PyPI, needs custom registry or verification
|
|
35
|
+
aws = []
|
|
36
|
+
all = ["codesecure-core[google,openai,aws]"]
|
|
37
|
+
|
|
38
|
+
[tool.setuptools.packages.find]
|
|
39
|
+
where = ["src"]
|
|
40
|
+
include = ["codesecure*"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CodeSecure - Enterprise Security Analysis Platform.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
7
|
+
except ImportError:
|
|
8
|
+
# Fallback for Python < 3.8
|
|
9
|
+
try:
|
|
10
|
+
from importlib_metadata import version, PackageNotFoundError
|
|
11
|
+
except ImportError:
|
|
12
|
+
version = lambda _: "unknown"
|
|
13
|
+
PackageNotFoundError = Exception
|
|
14
|
+
|
|
15
|
+
try:
|
|
16
|
+
__version__ = version("codesecure-core")
|
|
17
|
+
except (PackageNotFoundError, NameError):
|
|
18
|
+
# Fallback for development where the package is not installed
|
|
19
|
+
__version__ = "1.0.0b9"
|
|
File without changes
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import List, Dict, Any, Optional
|
|
3
|
+
|
|
4
|
+
from codesecure.common.models import Finding, EnhancedFinding
|
|
5
|
+
from codesecure.common.logging import get_logger
|
|
6
|
+
from codesecure.ai_providers.base import (
|
|
7
|
+
BaseAIProvider,
|
|
8
|
+
AIProviderType,
|
|
9
|
+
AIProviderStatus,
|
|
10
|
+
)
|
|
11
|
+
from codesecure.ai_providers.prompts import GenericPromptBuilder, GenericMarkdownParser
|
|
12
|
+
from codesecure.common.config import get_env
|
|
13
|
+
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AnthropicWrapper(BaseAIProvider):
|
|
18
|
+
"""Anthropic Provider Implementation"""
|
|
19
|
+
|
|
20
|
+
PROVIDER_TYPE = AIProviderType.AWS # Currently aliased/planned for AWS Bedrock or direct Anthropic
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
self.prompt_builder = GenericPromptBuilder()
|
|
24
|
+
self.parser = GenericMarkdownParser()
|
|
25
|
+
self._api_key = get_env("CODESECURE_ANTHROPIC_API_KEY", "")
|
|
26
|
+
self._model = get_env("CODESECURE_ANTHROPIC_MODEL", "claude-3-5-sonnet-20241022")
|
|
27
|
+
|
|
28
|
+
def _get_client(self):
|
|
29
|
+
try:
|
|
30
|
+
import anthropic
|
|
31
|
+
return anthropic.AsyncAnthropic(api_key=self._api_key)
|
|
32
|
+
except ImportError:
|
|
33
|
+
raise RuntimeError("anthropic SDK not installed")
|
|
34
|
+
|
|
35
|
+
def set_api_key(self, api_key: str):
|
|
36
|
+
self._api_key = api_key
|
|
37
|
+
|
|
38
|
+
def set_model(self, model: str):
|
|
39
|
+
self._model = model
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def is_available(self) -> bool:
|
|
43
|
+
try:
|
|
44
|
+
import anthropic
|
|
45
|
+
return bool(self._api_key)
|
|
46
|
+
except ImportError:
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
async def check_availability(self) -> tuple[AIProviderStatus, str]:
|
|
50
|
+
try:
|
|
51
|
+
import anthropic
|
|
52
|
+
except ImportError:
|
|
53
|
+
return AIProviderStatus.UNAVAILABLE, "Anthropic SDK not found (install with 'pip install anthropic')"
|
|
54
|
+
|
|
55
|
+
if not self._api_key:
|
|
56
|
+
return AIProviderStatus.UNAVAILABLE, "CODESECURE_ANTHROPIC_API_KEY is not set"
|
|
57
|
+
|
|
58
|
+
return AIProviderStatus.AVAILABLE, "Ready"
|
|
59
|
+
|
|
60
|
+
def _classify_error(self, err_msg: str, status_code: Optional[int] = None) -> str:
|
|
61
|
+
err_lower = err_msg.lower()
|
|
62
|
+
if status_code in (401, 403) or any(w in err_lower for w in ["invalid x-api-key", "authentication"]):
|
|
63
|
+
return "auth"
|
|
64
|
+
if status_code == 429 or any(w in err_lower for w in ["rate_limit", "too many requests"]):
|
|
65
|
+
return "rate_limit"
|
|
66
|
+
return "other"
|
|
67
|
+
|
|
68
|
+
async def test_connection(self) -> tuple[AIProviderStatus, str]:
|
|
69
|
+
status, msg = await self.check_availability()
|
|
70
|
+
if status != AIProviderStatus.AVAILABLE:
|
|
71
|
+
return status, msg
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
client = self._get_client()
|
|
75
|
+
logger.info("Validating Anthropic API key with model %s...", self._model)
|
|
76
|
+
await client.messages.create(
|
|
77
|
+
model=self._model,
|
|
78
|
+
messages=[{"role": "user", "content": "Reply with 'ok'"}],
|
|
79
|
+
max_tokens=5,
|
|
80
|
+
)
|
|
81
|
+
return AIProviderStatus.AVAILABLE, "Ready"
|
|
82
|
+
except Exception as e:
|
|
83
|
+
err_type = self._classify_error(str(e), getattr(e, "status_code", None))
|
|
84
|
+
if err_type == "auth":
|
|
85
|
+
return AIProviderStatus.ERROR, f"Anthropic Auth Error: {e}"
|
|
86
|
+
if err_type == "rate_limit":
|
|
87
|
+
return AIProviderStatus.ERROR, f"Anthropic Rate Limit Error: {e}"
|
|
88
|
+
return AIProviderStatus.ERROR, f"Anthropic Connection Failed: {str(e)}"
|
|
89
|
+
|
|
90
|
+
async def analyze_findings_batch(
|
|
91
|
+
self,
|
|
92
|
+
findings: List[Finding],
|
|
93
|
+
code_contexts: Dict[str, str],
|
|
94
|
+
batch_size: int = 10,
|
|
95
|
+
app_context: Optional[Dict[str, Any]] = None,
|
|
96
|
+
is_workspace_scan: bool = False
|
|
97
|
+
) -> List[EnhancedFinding]:
|
|
98
|
+
|
|
99
|
+
if not findings:
|
|
100
|
+
return []
|
|
101
|
+
|
|
102
|
+
client = self._get_client()
|
|
103
|
+
prompt = self.prompt_builder.build_batch_prompt(findings, app_context=app_context, is_workspace_scan=is_workspace_scan)
|
|
104
|
+
|
|
105
|
+
system_prompt = (
|
|
106
|
+
"You are CodeSecure, an expert AI security assistant. "
|
|
107
|
+
"Analyze security findings and provide remediation in Markdown format. "
|
|
108
|
+
"Follow the ZERO-CHATTER RULE: remediation code blocks must contain "
|
|
109
|
+
"ONLY pure source code, no explanations."
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
logger.info("Sending batch of %d findings to Anthropic (%s)...", len(findings), self._model)
|
|
114
|
+
|
|
115
|
+
response = await client.messages.create(
|
|
116
|
+
model=self._model,
|
|
117
|
+
system=system_prompt,
|
|
118
|
+
messages=[
|
|
119
|
+
{"role": "user", "content": prompt}
|
|
120
|
+
],
|
|
121
|
+
temperature=0.2,
|
|
122
|
+
max_tokens=4096,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
text = response.content[0].text if response.content else ""
|
|
126
|
+
logger.debug("Received AI response length: %d characters", len(text))
|
|
127
|
+
|
|
128
|
+
return self.parser.parse(text, findings)
|
|
129
|
+
|
|
130
|
+
except Exception as e:
|
|
131
|
+
err_type = self._classify_error(str(e), getattr(e, "status_code", None))
|
|
132
|
+
if err_type == "rate_limit":
|
|
133
|
+
raise RuntimeError(f"Anthropic Rate Limit Exceeded: {e}")
|
|
134
|
+
logger.exception("Anthropic Analysis failed: %s", e)
|
|
135
|
+
raise e
|
|
136
|
+
|
|
137
|
+
def enhance_finding(self, finding: Finding, analysis: Any) -> EnhancedFinding:
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
async def generate_stride_analysis(self, repo_path: Any, category: str) -> Dict[str, Any]:
|
|
141
|
+
return {"error": "Not implemented"}
|
|
142
|
+
|
|
143
|
+
def get_anthropic_wrapper() -> AnthropicWrapper:
|
|
144
|
+
return AnthropicWrapper()
|