contextwall 0.1.0__py3-none-any.whl
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.
- context_firewall/__init__.py +3 -0
- context_firewall/analytics/__init__.py +5 -0
- context_firewall/analytics/engine.py +234 -0
- context_firewall/api/__init__.py +5 -0
- context_firewall/api/app.py +1020 -0
- context_firewall/api/models.py +54 -0
- context_firewall/classifier/__init__.py +5 -0
- context_firewall/classifier/classifier.py +165 -0
- context_firewall/cli/__init__.py +5 -0
- context_firewall/cli/main.py +513 -0
- context_firewall/compliance/__init__.py +1 -0
- context_firewall/compliance/baa.py +57 -0
- context_firewall/compliance/chain.py +84 -0
- context_firewall/compliance/control_mappings.py +92 -0
- context_firewall/compliance/export.py +280 -0
- context_firewall/compliance/keys.py +94 -0
- context_firewall/config.py +234 -0
- context_firewall/control_plane/__init__.py +0 -0
- context_firewall/control_plane/client.py +107 -0
- context_firewall/control_plane/models.py +72 -0
- context_firewall/control_plane/pusher.py +377 -0
- context_firewall/daemon/__init__.py +5 -0
- context_firewall/daemon/main.py +461 -0
- context_firewall/db/__init__.py +6 -0
- context_firewall/db/connection.py +30 -0
- context_firewall/db/migrations.py +408 -0
- context_firewall/entropy/__init__.py +5 -0
- context_firewall/entropy/engine.py +271 -0
- context_firewall/graph/__init__.py +5 -0
- context_firewall/graph/engine.py +162 -0
- context_firewall/lint/__init__.py +0 -0
- context_firewall/lint/engine.py +334 -0
- context_firewall/mcp/__init__.py +5 -0
- context_firewall/mcp/server.py +237 -0
- context_firewall/metrics.py +100 -0
- context_firewall/models.py +130 -0
- context_firewall/policy/__init__.py +10 -0
- context_firewall/policy/detectors/__init__.py +0 -0
- context_firewall/policy/detectors/injection.py +480 -0
- context_firewall/policy/dsl/__init__.py +0 -0
- context_firewall/policy/dsl/evaluator.py +208 -0
- context_firewall/policy/dsl/loader.py +150 -0
- context_firewall/policy/dsl/types.py +112 -0
- context_firewall/policy/engine.py +532 -0
- context_firewall/provenance/__init__.py +5 -0
- context_firewall/provenance/engine.py +658 -0
- context_firewall/provenance/models.py +91 -0
- context_firewall/proxy/__init__.py +1 -0
- context_firewall/proxy/router.py +519 -0
- context_firewall/proxy/scanner.py +214 -0
- context_firewall/proxy/tokens.py +156 -0
- context_firewall/runtime/__init__.py +5 -0
- context_firewall/runtime/engine.py +303 -0
- context_firewall/source/__init__.py +1 -0
- context_firewall/source/registry.py +299 -0
- context_firewall/source/types.py +12 -0
- context_firewall/synthesizer/__init__.py +5 -0
- context_firewall/synthesizer/synthesizer.py +255 -0
- context_firewall/trust/__init__.py +5 -0
- context_firewall/trust/engine.py +139 -0
- context_firewall/trust/signals.py +184 -0
- contextwall-0.1.0.dist-info/METADATA +431 -0
- contextwall-0.1.0.dist-info/RECORD +66 -0
- contextwall-0.1.0.dist-info/WHEEL +4 -0
- contextwall-0.1.0.dist-info/entry_points.txt +3 -0
- contextwall-0.1.0.dist-info/licenses/LICENSE +33 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""REST API request/response Pydantic models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
DEFAULT_TRUST_CUTOFF: float = 0.30
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AnalyzeOptions(BaseModel):
|
|
11
|
+
max_candidates: int = 20
|
|
12
|
+
trust_cutoff: float = DEFAULT_TRUST_CUTOFF
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AnalyzeRequest(BaseModel):
|
|
16
|
+
task: str
|
|
17
|
+
session_id: str | None = None
|
|
18
|
+
repository_root: str | None = None
|
|
19
|
+
options: AnalyzeOptions = AnalyzeOptions()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class BundleOptions(BaseModel):
|
|
23
|
+
token_budget: int | None = None
|
|
24
|
+
trust_cutoff: float = DEFAULT_TRUST_CUTOFF
|
|
25
|
+
include_summary: bool = True
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BundleRequest(BaseModel):
|
|
29
|
+
task: str
|
|
30
|
+
session_id: str | None = None
|
|
31
|
+
repository_root: str | None = None
|
|
32
|
+
options: BundleOptions = BundleOptions()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class OutcomeRequest(BaseModel):
|
|
36
|
+
session_id: str
|
|
37
|
+
request_id: str
|
|
38
|
+
outcome_type: str
|
|
39
|
+
success: bool
|
|
40
|
+
score: float
|
|
41
|
+
node_ids: list[str] = []
|
|
42
|
+
source_id: str = ""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class APIToken(BaseModel):
|
|
46
|
+
token: str
|
|
47
|
+
name: str
|
|
48
|
+
scopes: list[str]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ErrorResponse(BaseModel):
|
|
52
|
+
error: str
|
|
53
|
+
code: str
|
|
54
|
+
request_id: str = ""
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Intent Classifier implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
import uuid
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
|
|
10
|
+
from context_firewall.config import Config
|
|
11
|
+
from context_firewall.models import (
|
|
12
|
+
ClassificationResult,
|
|
13
|
+
PipelineContext,
|
|
14
|
+
TaskType,
|
|
15
|
+
TraversalStrategy,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
_SECURITY_REVIEW_KEYWORDS = frozenset([
|
|
21
|
+
"security", "audit", "vulnerability", "cve", "exploit", "injection",
|
|
22
|
+
"xss", "csrf", "auth", "authentication", "authorization", "permission",
|
|
23
|
+
"sanitize", "validate input", "secret", "credential", "token leak",
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
_DEPENDENCY_AUDIT_KEYWORDS = frozenset([
|
|
27
|
+
"dependency", "dependencies", "package", "import", "require",
|
|
28
|
+
"go.mod", "package.json", "requirements.txt", "sbom", "supply chain",
|
|
29
|
+
"license", "outdated", "upgrade package",
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
_AUGMENTED_KEYWORDS: dict[TaskType, list[str]] = {
|
|
33
|
+
TaskType.SECURITY_REVIEW: [
|
|
34
|
+
"auth", "middleware", "token", "session", "permission", "sanitize", "validate",
|
|
35
|
+
],
|
|
36
|
+
TaskType.DEPENDENCY_AUDIT: [
|
|
37
|
+
"import", "require", "go.mod", "package.json", "requirements.txt", "sbom",
|
|
38
|
+
],
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_TRAVERSAL_STRATEGIES: dict[TaskType, TraversalStrategy] = {
|
|
42
|
+
TaskType.BUG_FIX: TraversalStrategy(
|
|
43
|
+
edge_types=["calls", "imports"],
|
|
44
|
+
direction="both",
|
|
45
|
+
max_depth=4,
|
|
46
|
+
),
|
|
47
|
+
TaskType.NEW_FEATURE: TraversalStrategy(
|
|
48
|
+
edge_types=["calls", "imports", "inherits"],
|
|
49
|
+
direction="both",
|
|
50
|
+
max_depth=4,
|
|
51
|
+
),
|
|
52
|
+
TaskType.REFACTOR: TraversalStrategy(
|
|
53
|
+
edge_types=["calls", "imports", "inherits", "implements"],
|
|
54
|
+
direction="both",
|
|
55
|
+
max_depth=4,
|
|
56
|
+
),
|
|
57
|
+
TaskType.SECURITY_REVIEW: TraversalStrategy(
|
|
58
|
+
edge_types=["calls", "imports", "uses"],
|
|
59
|
+
direction="inbound",
|
|
60
|
+
max_depth=3,
|
|
61
|
+
),
|
|
62
|
+
TaskType.DEPENDENCY_AUDIT: TraversalStrategy(
|
|
63
|
+
edge_types=["imports", "requires"],
|
|
64
|
+
direction="outbound",
|
|
65
|
+
max_depth=2,
|
|
66
|
+
),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_WEIGHT_PROFILES: dict[TaskType, str] = {
|
|
70
|
+
TaskType.BUG_FIX: "bug_fix",
|
|
71
|
+
TaskType.NEW_FEATURE: "new_feature",
|
|
72
|
+
TaskType.REFACTOR: "refactor",
|
|
73
|
+
TaskType.SECURITY_REVIEW: "security_review",
|
|
74
|
+
TaskType.DEPENDENCY_AUDIT: "dependency_audit",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _detect_cre_extension(task: str) -> TaskType | None:
|
|
79
|
+
lower = task.lower()
|
|
80
|
+
security_hits = sum(1 for kw in _SECURITY_REVIEW_KEYWORDS if kw in lower)
|
|
81
|
+
dependency_hits = sum(1 for kw in _DEPENDENCY_AUDIT_KEYWORDS if kw in lower)
|
|
82
|
+
if security_hits >= 2:
|
|
83
|
+
return TaskType.SECURITY_REVIEW
|
|
84
|
+
if dependency_hits >= 2:
|
|
85
|
+
return TaskType.DEPENDENCY_AUDIT
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _classify_via_context_compiler(task: str) -> ClassificationResult:
|
|
90
|
+
try:
|
|
91
|
+
from context_compiler.retrieval.classifier import classify, extract_query_keywords
|
|
92
|
+
from context_compiler.models import TaskType as CCTaskType
|
|
93
|
+
|
|
94
|
+
cc_task_type = classify(task)
|
|
95
|
+
keywords = extract_query_keywords(task)
|
|
96
|
+
task_type_map = {
|
|
97
|
+
"BUG_FIX": TaskType.BUG_FIX,
|
|
98
|
+
"NEW_FEATURE": TaskType.NEW_FEATURE,
|
|
99
|
+
"REFACTOR": TaskType.REFACTOR,
|
|
100
|
+
}
|
|
101
|
+
task_type = task_type_map.get(cc_task_type.value, TaskType.NEW_FEATURE)
|
|
102
|
+
return ClassificationResult(
|
|
103
|
+
task_type=task_type,
|
|
104
|
+
confidence=1.0,
|
|
105
|
+
keywords=list(keywords),
|
|
106
|
+
source="context-compiler",
|
|
107
|
+
)
|
|
108
|
+
except Exception as e:
|
|
109
|
+
logger.warning("context-compiler classify failed, using fallback", extra={"error": str(e)})
|
|
110
|
+
return ClassificationResult(
|
|
111
|
+
task_type=TaskType.NEW_FEATURE,
|
|
112
|
+
confidence=0.0,
|
|
113
|
+
keywords=[],
|
|
114
|
+
source="fallback",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def classify_task(task: str) -> ClassificationResult:
|
|
119
|
+
t0 = time.monotonic()
|
|
120
|
+
|
|
121
|
+
cre_type = _detect_cre_extension(task)
|
|
122
|
+
if cre_type is not None:
|
|
123
|
+
augmented = _AUGMENTED_KEYWORDS.get(cre_type, [])
|
|
124
|
+
result = ClassificationResult(
|
|
125
|
+
task_type=cre_type,
|
|
126
|
+
confidence=1.0,
|
|
127
|
+
keywords=augmented,
|
|
128
|
+
source="cre-extension",
|
|
129
|
+
)
|
|
130
|
+
else:
|
|
131
|
+
result = _classify_via_context_compiler(task)
|
|
132
|
+
augmented = _AUGMENTED_KEYWORDS.get(result.task_type, [])
|
|
133
|
+
if augmented:
|
|
134
|
+
result = result.model_copy(update={"keywords": result.keywords + augmented})
|
|
135
|
+
|
|
136
|
+
elapsed_ms = (time.monotonic() - t0) * 1000
|
|
137
|
+
logger.debug("classify_task", extra={"task_type": result.task_type, "latency_ms": elapsed_ms})
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def build_pipeline_context(
|
|
142
|
+
task: str,
|
|
143
|
+
classification: ClassificationResult,
|
|
144
|
+
session_id: str,
|
|
145
|
+
config: Config,
|
|
146
|
+
) -> PipelineContext:
|
|
147
|
+
strategy = _TRAVERSAL_STRATEGIES.get(
|
|
148
|
+
classification.task_type,
|
|
149
|
+
TraversalStrategy(edge_types=["calls", "imports"], direction="both", max_depth=4),
|
|
150
|
+
)
|
|
151
|
+
# respect config depth override
|
|
152
|
+
if config.graph.max_depth != 4:
|
|
153
|
+
strategy = strategy.model_copy(update={"max_depth": config.graph.max_depth})
|
|
154
|
+
|
|
155
|
+
return PipelineContext(
|
|
156
|
+
task=task,
|
|
157
|
+
task_type=classification.task_type,
|
|
158
|
+
confidence=classification.confidence,
|
|
159
|
+
keywords=classification.keywords,
|
|
160
|
+
traversal_strategy=strategy,
|
|
161
|
+
trust_weight_profile=_WEIGHT_PROFILES.get(classification.task_type, "new_feature"),
|
|
162
|
+
request_id=str(uuid.uuid4()),
|
|
163
|
+
session_id=session_id,
|
|
164
|
+
received_at=datetime.utcnow(),
|
|
165
|
+
)
|