codegraph-engine 3.0.0__1-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.
- codegraph/__init__.py +37 -0
- codegraph/agent.py +26 -0
- codegraph/agent_brain.py +2212 -0
- codegraph/agent_capabilities.py +4058 -0
- codegraph/agent_rules.py +531 -0
- codegraph/api_drift.py +455 -0
- codegraph/architecture.py +371 -0
- codegraph/async_queue/__init__.py +50 -0
- codegraph/async_queue/detectors/__init__.py +48 -0
- codegraph/async_queue/detectors/celery.py +160 -0
- codegraph/async_queue/detectors/kafka.py +168 -0
- codegraph/async_queue/detectors/rabbitmq.py +116 -0
- codegraph/async_queue/detectors/redis.py +130 -0
- codegraph/async_queue/detectors/sqs.py +135 -0
- codegraph/async_queue/models.py +161 -0
- codegraph/async_queue/otel_models.py +74 -0
- codegraph/async_queue/otel_parser.py +337 -0
- codegraph/async_queue/reconciler.py +427 -0
- codegraph/async_queue/scanner.py +291 -0
- codegraph/async_queue/traversal.py +447 -0
- codegraph/audit.py +106 -0
- codegraph/binding_resolver.py +616 -0
- codegraph/branch_comparison.py +197 -0
- codegraph/cache.py +95 -0
- codegraph/change_impact.py +547 -0
- codegraph/cli.py +2436 -0
- codegraph/cli_output.py +205 -0
- codegraph/config.py +45 -0
- codegraph/constraints.py +238 -0
- codegraph/context.py +2182 -0
- codegraph/context_freshness.py +351 -0
- codegraph/database/__init__.py +72 -0
- codegraph/database/extractor.py +3651 -0
- codegraph/database/interrogation.py +1494 -0
- codegraph/database/models.py +310 -0
- codegraph/database/schema_drift.py +862 -0
- codegraph/epistemic.py +200 -0
- codegraph/errors.py +282 -0
- codegraph/evidence/__init__.py +15 -0
- codegraph/evidence/citations.py +418 -0
- codegraph/evidence_contract.py +486 -0
- codegraph/frameworks.py +960 -0
- codegraph/frameworks_nestjs.py +502 -0
- codegraph/frameworks_nextjs.py +337 -0
- codegraph/frameworks_react.py +457 -0
- codegraph/freshness.py +335 -0
- codegraph/frontend_html.py +109 -0
- codegraph/git.py +362 -0
- codegraph/git_state.py +545 -0
- codegraph/graph/__init__.py +46 -0
- codegraph/graph/models.py +56 -0
- codegraph/graph/traversal.py +1993 -0
- codegraph/indexing/__init__.py +24 -0
- codegraph/indexing/classifier.py +564 -0
- codegraph/indexing/indexer.py +2578 -0
- codegraph/indexing/models.py +427 -0
- codegraph/indexing/parser.py +3404 -0
- codegraph/indexing/pool.py +136 -0
- codegraph/indexing/scanner.py +211 -0
- codegraph/indexing/telemetry.py +326 -0
- codegraph/indexing/templates.py +418 -0
- codegraph/indexing/test_framework.py +116 -0
- codegraph/installer.py +1702 -0
- codegraph/interrogation.py +2355 -0
- codegraph/llm/__init__.py +3 -0
- codegraph/llm/base.py +15 -0
- codegraph/llm/context.py +20 -0
- codegraph/mcp/__init__.py +3 -0
- codegraph/mcp/server.py +1476 -0
- codegraph/mcp_diagnostics.py +882 -0
- codegraph/memory/__init__.py +3 -0
- codegraph/memory/store.py +46 -0
- codegraph/models.py +310 -0
- codegraph/monorepo.py +925 -0
- codegraph/observability.py +262 -0
- codegraph/optimizer.py +1014 -0
- codegraph/planner.py +417 -0
- codegraph/process_lifecycle.py +1153 -0
- codegraph/py.typed +1 -0
- codegraph/query_expansion.py +199 -0
- codegraph/ranking.py +408 -0
- codegraph/refactor/__init__.py +36 -0
- codegraph/refactor/models.py +114 -0
- codegraph/refactor/renamer.py +844 -0
- codegraph/refactor/transactions.py +414 -0
- codegraph/resolver.py +3968 -0
- codegraph/resources/__init__.py +46 -0
- codegraph/resources/cache.py +117 -0
- codegraph/resources/coalescer.py +83 -0
- codegraph/resources/debouncer.py +98 -0
- codegraph/resources/governor.py +219 -0
- codegraph/resources/monitor.py +102 -0
- codegraph/resources/policy.py +123 -0
- codegraph/retrieval_policy.py +320 -0
- codegraph/roblox.py +2174 -0
- codegraph/route_composer.py +363 -0
- codegraph/route_topology.py +172 -0
- codegraph/runtime/__init__.py +51 -0
- codegraph/runtime/collector.py +155 -0
- codegraph/runtime/db_watcher.py +191 -0
- codegraph/runtime/ingestor.py +692 -0
- codegraph/runtime/models.py +130 -0
- codegraph/runtime/port_inspector.py +304 -0
- codegraph/runtime/reconciliation.py +492 -0
- codegraph/runtime/runner.py +353 -0
- codegraph/runtime/trace_linker.py +175 -0
- codegraph/search/__init__.py +25 -0
- codegraph/search/hybrid.py +846 -0
- codegraph/search/semantic.py +28 -0
- codegraph/security/__init__.py +51 -0
- codegraph/security/paths.py +189 -0
- codegraph/security/redaction.py +633 -0
- codegraph/semantic_conflicts.py +270 -0
- codegraph/semantic_decorators.py +372 -0
- codegraph/structural_diff.py +542 -0
- codegraph/styles.py +144 -0
- codegraph/symbol_history.py +277 -0
- codegraph/target_resolver.py +348 -0
- codegraph/task.py +637 -0
- codegraph/tool_selection_eval.py +1158 -0
- codegraph/tsconfig.py +236 -0
- codegraph/ui/__init__.py +6 -0
- codegraph/ui/server.py +755 -0
- codegraph/watcher.py +323 -0
- codegraph_engine-3.0.0.dist-info/METADATA +859 -0
- codegraph_engine-3.0.0.dist-info/RECORD +130 -0
- codegraph_engine-3.0.0.dist-info/WHEEL +6 -0
- codegraph_engine-3.0.0.dist-info/entry_points.txt +2 -0
- codegraph_engine-3.0.0.dist-info/licenses/LICENSE +21 -0
- codegraph_engine-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SemanticSearchUnavailable(RuntimeError):
|
|
8
|
+
"""No embedding provider has been configured for semantic retrieval."""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EmbeddingProvider(ABC):
|
|
12
|
+
"""Provider-neutral embeddings interface; implementations must be deterministic in tests."""
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def embed(self, texts: list[str]) -> list[list[float]]:
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class SemanticStatus:
|
|
21
|
+
available: bool
|
|
22
|
+
detail: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def status(provider: EmbeddingProvider | None) -> SemanticStatus:
|
|
26
|
+
if provider is None:
|
|
27
|
+
return SemanticStatus(False, "No embedding provider configured; lexical and symbol retrieval remain active.")
|
|
28
|
+
return SemanticStatus(True, "Embedding provider configured.")
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from .paths import (
|
|
2
|
+
DEFAULT_MAX_READ_BYTES,
|
|
3
|
+
DEFAULT_SENSITIVE_PATTERNS,
|
|
4
|
+
is_sensitive,
|
|
5
|
+
is_sensitive_path,
|
|
6
|
+
safe_path,
|
|
7
|
+
safe_read_text,
|
|
8
|
+
)
|
|
9
|
+
from .redaction import (
|
|
10
|
+
REDACTED_DB_CREDENTIALS,
|
|
11
|
+
REDACTED_PRIVATE_KEY,
|
|
12
|
+
REDACTED_SECRET,
|
|
13
|
+
REDACTED_TOKEN,
|
|
14
|
+
SECRET_ENV_EXACT_NAMES,
|
|
15
|
+
SENSITIVE_RUNTIME_KEYS,
|
|
16
|
+
audit_sqlite_for_secrets,
|
|
17
|
+
contains_private_key,
|
|
18
|
+
detect_env_variable_reads,
|
|
19
|
+
is_probable_secret_value,
|
|
20
|
+
is_secret_variable_name,
|
|
21
|
+
normalize_and_redact_sql,
|
|
22
|
+
parse_safe_connection_metadata,
|
|
23
|
+
redact_connection_string,
|
|
24
|
+
redact_payload,
|
|
25
|
+
redact_secrets,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"DEFAULT_MAX_READ_BYTES",
|
|
30
|
+
"DEFAULT_SENSITIVE_PATTERNS",
|
|
31
|
+
"REDACTED_DB_CREDENTIALS",
|
|
32
|
+
"REDACTED_PRIVATE_KEY",
|
|
33
|
+
"REDACTED_SECRET",
|
|
34
|
+
"REDACTED_TOKEN",
|
|
35
|
+
"SECRET_ENV_EXACT_NAMES",
|
|
36
|
+
"SENSITIVE_RUNTIME_KEYS",
|
|
37
|
+
"audit_sqlite_for_secrets",
|
|
38
|
+
"contains_private_key",
|
|
39
|
+
"detect_env_variable_reads",
|
|
40
|
+
"is_probable_secret_value",
|
|
41
|
+
"is_secret_variable_name",
|
|
42
|
+
"is_sensitive",
|
|
43
|
+
"is_sensitive_path",
|
|
44
|
+
"normalize_and_redact_sql",
|
|
45
|
+
"parse_safe_connection_metadata",
|
|
46
|
+
"redact_connection_string",
|
|
47
|
+
"redact_payload",
|
|
48
|
+
"redact_secrets",
|
|
49
|
+
"safe_path",
|
|
50
|
+
"safe_read_text",
|
|
51
|
+
]
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import fnmatch
|
|
4
|
+
import re
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from urllib.parse import unquote
|
|
7
|
+
|
|
8
|
+
from codegraph.errors import ErrorCode, SecurityError
|
|
9
|
+
|
|
10
|
+
DEFAULT_MAX_READ_BYTES = 256_000
|
|
11
|
+
|
|
12
|
+
DEFAULT_SENSITIVE_PATTERNS: tuple[str, ...] = (
|
|
13
|
+
".env",
|
|
14
|
+
".env.*",
|
|
15
|
+
"*.pem",
|
|
16
|
+
"*.key",
|
|
17
|
+
"*.crt",
|
|
18
|
+
"*.p12",
|
|
19
|
+
"*.pfx",
|
|
20
|
+
"id_rsa",
|
|
21
|
+
"id_rsa.*",
|
|
22
|
+
"id_ed25519",
|
|
23
|
+
"id_ed25519.*",
|
|
24
|
+
"id_ecdsa",
|
|
25
|
+
"id_ecdsa.*",
|
|
26
|
+
"id_dsa",
|
|
27
|
+
"id_dsa.*",
|
|
28
|
+
"kubeconfig",
|
|
29
|
+
"*kubeconfig*",
|
|
30
|
+
".npmrc",
|
|
31
|
+
".pypirc",
|
|
32
|
+
".netrc",
|
|
33
|
+
".git/config",
|
|
34
|
+
"*/.git/config",
|
|
35
|
+
".git/credentials",
|
|
36
|
+
"*/.git/credentials",
|
|
37
|
+
".git-credentials",
|
|
38
|
+
"credentials",
|
|
39
|
+
"credentials*",
|
|
40
|
+
"*.credentials",
|
|
41
|
+
"secrets.*",
|
|
42
|
+
"*secret*.json",
|
|
43
|
+
"*secret*.yaml",
|
|
44
|
+
"*secret*.yml",
|
|
45
|
+
"service-account*",
|
|
46
|
+
"service_account*",
|
|
47
|
+
".aws/*",
|
|
48
|
+
"*/.aws/*",
|
|
49
|
+
".ssh/*",
|
|
50
|
+
"*/.ssh/*",
|
|
51
|
+
".gnupg/*",
|
|
52
|
+
"*/.gnupg/*",
|
|
53
|
+
".kube/*",
|
|
54
|
+
"*/.kube/*",
|
|
55
|
+
".docker/config.json",
|
|
56
|
+
"*/.docker/config.json",
|
|
57
|
+
"*docker*credential*",
|
|
58
|
+
"docker-credentials*",
|
|
59
|
+
"*.sqlite",
|
|
60
|
+
"*.sqlite3",
|
|
61
|
+
"*.db",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
_SENSITIVE_DIRS: frozenset[str] = frozenset({
|
|
65
|
+
".aws",
|
|
66
|
+
".ssh",
|
|
67
|
+
".gnupg",
|
|
68
|
+
".kube",
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
_DEFAULT_SENSITIVE_REGEX = re.compile(
|
|
72
|
+
"|".join(f"(?:{fnmatch.translate(p.lower())})" for p in DEFAULT_SENSITIVE_PATTERNS)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def is_sensitive_path(
|
|
77
|
+
relative_path: str | Path,
|
|
78
|
+
patterns: tuple[str, ...] = DEFAULT_SENSITIVE_PATTERNS,
|
|
79
|
+
) -> bool:
|
|
80
|
+
"""Authoritative check for sensitive credentials, private keys, and secret configs."""
|
|
81
|
+
raw = str(relative_path).replace("\\", "/").strip()
|
|
82
|
+
while raw.startswith("./"):
|
|
83
|
+
raw = raw[2:]
|
|
84
|
+
p_obj = Path(raw)
|
|
85
|
+
value = p_obj.as_posix()
|
|
86
|
+
value_lower = value.lower()
|
|
87
|
+
name = p_obj.name
|
|
88
|
+
name_lower = name.lower()
|
|
89
|
+
|
|
90
|
+
parts_lower = {part.lower() for part in p_obj.parts}
|
|
91
|
+
if parts_lower & _SENSITIVE_DIRS:
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
if value_lower in (".git/config", ".git/credentials") or value_lower.endswith(("/.git/config", "/.git/credentials")):
|
|
95
|
+
return True
|
|
96
|
+
if value_lower == ".docker/config.json" or value_lower.endswith("/.docker/config.json"):
|
|
97
|
+
return True
|
|
98
|
+
|
|
99
|
+
if patterns is DEFAULT_SENSITIVE_PATTERNS or patterns == DEFAULT_SENSITIVE_PATTERNS:
|
|
100
|
+
return bool(
|
|
101
|
+
_DEFAULT_SENSITIVE_REGEX.match(value_lower)
|
|
102
|
+
or _DEFAULT_SENSITIVE_REGEX.match(name_lower)
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
return any(
|
|
106
|
+
fnmatch.fnmatch(value, p)
|
|
107
|
+
or fnmatch.fnmatch(value_lower, p.lower())
|
|
108
|
+
or fnmatch.fnmatch(name, p)
|
|
109
|
+
or fnmatch.fnmatch(name_lower, p.lower())
|
|
110
|
+
for p in patterns
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def is_sensitive(
|
|
115
|
+
relative_path: str | Path,
|
|
116
|
+
patterns: tuple[str, ...] = DEFAULT_SENSITIVE_PATTERNS,
|
|
117
|
+
) -> bool:
|
|
118
|
+
"""Alias for `is_sensitive_path` maintained for backward compatibility."""
|
|
119
|
+
return is_sensitive_path(relative_path, patterns=patterns)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def safe_path(repository: Path, requested: str | Path) -> Path:
|
|
123
|
+
"""Resolve a repo-relative path and reject traversal and symlink escapes."""
|
|
124
|
+
root = repository.resolve(strict=True)
|
|
125
|
+
raw_value = unquote(str(requested))
|
|
126
|
+
if "\x00" in raw_value:
|
|
127
|
+
raise SecurityError("null bytes are not allowed in paths")
|
|
128
|
+
raw = Path(raw_value)
|
|
129
|
+
if raw.is_absolute():
|
|
130
|
+
raise SecurityError("absolute paths are not allowed")
|
|
131
|
+
candidate = (root / raw).resolve(strict=False)
|
|
132
|
+
try:
|
|
133
|
+
candidate.relative_to(root)
|
|
134
|
+
except ValueError as exc:
|
|
135
|
+
raise SecurityError("path escapes the configured repository") from exc
|
|
136
|
+
return candidate
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def safe_read_text(
|
|
140
|
+
repository: Path,
|
|
141
|
+
requested: str | Path,
|
|
142
|
+
max_read_bytes: int = DEFAULT_MAX_READ_BYTES,
|
|
143
|
+
) -> tuple[Path, str, str]:
|
|
144
|
+
"""Safely validate and read a repository file with path-traversal, sensitive-path,
|
|
145
|
+
binary-file, and size-cap enforcement BEFORE reading full contents into memory.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
(resolved_path, relative_posix_path, text_content)
|
|
149
|
+
"""
|
|
150
|
+
resolved = safe_path(repository, requested)
|
|
151
|
+
root = repository.resolve(strict=True)
|
|
152
|
+
relative = resolved.relative_to(root)
|
|
153
|
+
rel_posix = relative.as_posix()
|
|
154
|
+
|
|
155
|
+
if is_sensitive_path(relative) or is_sensitive_path( str(requested) ):
|
|
156
|
+
raise SecurityError(
|
|
157
|
+
f"sensitive files cannot be read: '{rel_posix}'",
|
|
158
|
+
code=ErrorCode.SENSITIVE_FILE_ACCESS_DENIED,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
if not resolved.exists() or not resolved.is_file():
|
|
162
|
+
raise FileNotFoundError(f"File does not exist: '{rel_posix}'")
|
|
163
|
+
|
|
164
|
+
file_size = resolved.stat().st_size
|
|
165
|
+
if file_size > max_read_bytes:
|
|
166
|
+
raise SecurityError(
|
|
167
|
+
f"File '{rel_posix}' size ({file_size} bytes) exceeds max_read_bytes limit ({max_read_bytes} bytes).",
|
|
168
|
+
code=ErrorCode.FILE_TOO_LARGE,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
with resolved.open("rb") as fh:
|
|
172
|
+
raw_bytes = fh.read(max_read_bytes + 1)
|
|
173
|
+
|
|
174
|
+
if len(raw_bytes) > max_read_bytes:
|
|
175
|
+
raise SecurityError(
|
|
176
|
+
f"File '{rel_posix}' exceeds max_read_bytes limit ({max_read_bytes} bytes).",
|
|
177
|
+
code=ErrorCode.FILE_TOO_LARGE,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
if b"\x00" in raw_bytes[:8192]:
|
|
181
|
+
raise SecurityError(
|
|
182
|
+
f"Binary file '{rel_posix}' cannot be read as text.",
|
|
183
|
+
code=ErrorCode.BINARY_FILE_NOT_READABLE,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
from .redaction import redact_secrets
|
|
187
|
+
|
|
188
|
+
decoded = raw_bytes.decode("utf-8", errors="replace")
|
|
189
|
+
return resolved, rel_posix, redact_secrets(decoded)
|