vigil-codeintel 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.
Files changed (131) hide show
  1. vigil_codeintel-0.1.0.dist-info/METADATA +780 -0
  2. vigil_codeintel-0.1.0.dist-info/RECORD +131 -0
  3. vigil_codeintel-0.1.0.dist-info/WHEEL +5 -0
  4. vigil_codeintel-0.1.0.dist-info/entry_points.txt +3 -0
  5. vigil_codeintel-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. vigil_codeintel-0.1.0.dist-info/top_level.txt +3 -0
  7. vigil_forensic/__init__.py +224 -0
  8. vigil_forensic/_git_utils.py +178 -0
  9. vigil_forensic/_shared.py +510 -0
  10. vigil_forensic/_stubs.py +156 -0
  11. vigil_forensic/gate_checks/__init__.py +1 -0
  12. vigil_forensic/gate_checks/_ast_helpers.py +629 -0
  13. vigil_forensic/gate_checks/_deployment_detector.py +573 -0
  14. vigil_forensic/gate_checks/atomic_write_checks.py +1143 -0
  15. vigil_forensic/gate_checks/authority_checks.py +95 -0
  16. vigil_forensic/gate_checks/boundary_breach_checks.py +202 -0
  17. vigil_forensic/gate_checks/broad_except_checks.py +301 -0
  18. vigil_forensic/gate_checks/broad_except_hidden_sentinel_checks.py +365 -0
  19. vigil_forensic/gate_checks/common.py +253 -0
  20. vigil_forensic/gate_checks/config_safety_checks.py +704 -0
  21. vigil_forensic/gate_checks/config_ssot_checks.py +78 -0
  22. vigil_forensic/gate_checks/conflict_checks.py +193 -0
  23. vigil_forensic/gate_checks/context_fallback_checks.py +697 -0
  24. vigil_forensic/gate_checks/context_health_checks.py +289 -0
  25. vigil_forensic/gate_checks/contract_shape_drift_checks.py +459 -0
  26. vigil_forensic/gate_checks/dirty_baseline_check.py +274 -0
  27. vigil_forensic/gate_checks/duplication_checks.py +387 -0
  28. vigil_forensic/gate_checks/embedded_string_checks.py +123 -0
  29. vigil_forensic/gate_checks/empty_output_checks.py +87 -0
  30. vigil_forensic/gate_checks/encoding_checks.py +847 -0
  31. vigil_forensic/gate_checks/export_completeness_checks.py +156 -0
  32. vigil_forensic/gate_checks/fallback_checks.py +41 -0
  33. vigil_forensic/gate_checks/file_proliferation_checks.py +171 -0
  34. vigil_forensic/gate_checks/fix_without_test_checks.py +69 -0
  35. vigil_forensic/gate_checks/forensic_cluster_runners/__init__.py +9 -0
  36. vigil_forensic/gate_checks/forensic_cluster_runners/_helpers.py +71 -0
  37. vigil_forensic/gate_checks/forensic_cluster_runners/advanced_checks.py +322 -0
  38. vigil_forensic/gate_checks/forensic_cluster_runners/core.py +273 -0
  39. vigil_forensic/gate_checks/forensic_cluster_runners/integrity_checks.py +203 -0
  40. vigil_forensic/gate_checks/forensic_cluster_runners/quality_checks.py +666 -0
  41. vigil_forensic/gate_checks/forensic_clusters/__init__.py +193 -0
  42. vigil_forensic/gate_checks/forensic_clusters/allowlist.py +426 -0
  43. vigil_forensic/gate_checks/forensic_clusters/allowlist_writer.py +302 -0
  44. vigil_forensic/gate_checks/forensic_clusters/api_protocol.py +231 -0
  45. vigil_forensic/gate_checks/forensic_clusters/async_quality.py +1156 -0
  46. vigil_forensic/gate_checks/forensic_clusters/code_style.py +808 -0
  47. vigil_forensic/gate_checks/forensic_clusters/core.py +319 -0
  48. vigil_forensic/gate_checks/forensic_clusters/data_quality.py +763 -0
  49. vigil_forensic/gate_checks/forensic_clusters/dead_code.py +480 -0
  50. vigil_forensic/gate_checks/forensic_clusters/edit_mutation.py +842 -0
  51. vigil_forensic/gate_checks/forensic_clusters/exception_boundary.py +240 -0
  52. vigil_forensic/gate_checks/forensic_clusters/legacy_debt.py +556 -0
  53. vigil_forensic/gate_checks/forensic_clusters/static_analysis.py +834 -0
  54. vigil_forensic/gate_checks/forensic_clusters/structural_quality.py +298 -0
  55. vigil_forensic/gate_checks/god_object_zones_checks.py +173 -0
  56. vigil_forensic/gate_checks/hallucination_checks.py +566 -0
  57. vigil_forensic/gate_checks/hunter_artifact_completeness_check.py +139 -0
  58. vigil_forensic/gate_checks/implementation_overfit_checks.py +380 -0
  59. vigil_forensic/gate_checks/import_integrity_checks.py +233 -0
  60. vigil_forensic/gate_checks/imports_in_function_checks.py +283 -0
  61. vigil_forensic/gate_checks/ml_checks.py +318 -0
  62. vigil_forensic/gate_checks/performance_checks.py +106 -0
  63. vigil_forensic/gate_checks/project_specific_runner.py +691 -0
  64. vigil_forensic/gate_checks/provider_capability_checks.py +73 -0
  65. vigil_forensic/gate_checks/refactor_completeness_checks.py +274 -0
  66. vigil_forensic/gate_checks/reliability_checks.py +389 -0
  67. vigil_forensic/gate_checks/reporting_checks.py +55 -0
  68. vigil_forensic/gate_checks/runtime_behavior_checks.py +220 -0
  69. vigil_forensic/gate_checks/security_injection_checks.py +332 -0
  70. vigil_forensic/gate_checks/semantic_intent_checks.py +139 -0
  71. vigil_forensic/gate_checks/size_complexity_checks.py +336 -0
  72. vigil_forensic/gate_checks/stuck_feature_flag_checks.py +354 -0
  73. vigil_forensic/gate_checks/syntax_validity_checks.py +217 -0
  74. vigil_forensic/gate_checks/temporal_freshness_checks.py +79 -0
  75. vigil_forensic/gate_checks/test_quality_checks.py +946 -0
  76. vigil_forensic/gate_checks/testing_checks.py +149 -0
  77. vigil_forensic/gate_checks/toctou_checks.py +367 -0
  78. vigil_forensic/gate_checks/type_checking_checks.py +316 -0
  79. vigil_forensic/gate_models.py +392 -0
  80. vigil_forensic/gate_packs/__init__.py +1 -0
  81. vigil_forensic/gate_packs/universal.py +179 -0
  82. vigil_forensic/gate_profile.json +31 -0
  83. vigil_forensic/gate_registry.py +21 -0
  84. vigil_forensic/language_profiles.py +219 -0
  85. vigil_forensic/meta_findings.py +207 -0
  86. vigil_forensic/self_audit.py +725 -0
  87. vigil_forensic/source_analysis.py +175 -0
  88. vigil_mapper/__init__.py +103 -0
  89. vigil_mapper/_ast_helpers_minimal.py +229 -0
  90. vigil_mapper/_extract_imports_impl.py +123 -0
  91. vigil_mapper/_file_count_guard.py +129 -0
  92. vigil_mapper/_git_utils.py +178 -0
  93. vigil_mapper/_runtime_ast.py +438 -0
  94. vigil_mapper/_runtime_dispatch.py +137 -0
  95. vigil_mapper/_seed_helpers.py +82 -0
  96. vigil_mapper/authority_builder.py +1102 -0
  97. vigil_mapper/cli_entry.py +731 -0
  98. vigil_mapper/conflict_builder.py +818 -0
  99. vigil_mapper/data_contract_builder.py +446 -0
  100. vigil_mapper/findings_builder.py +716 -0
  101. vigil_mapper/fingerprint.py +53 -0
  102. vigil_mapper/hotspot_builder.py +539 -0
  103. vigil_mapper/map_common.py +449 -0
  104. vigil_mapper/map_errors.py +55 -0
  105. vigil_mapper/map_models.py +431 -0
  106. vigil_mapper/map_models_ext.py +206 -0
  107. vigil_mapper/map_models_findings.py +130 -0
  108. vigil_mapper/map_storage.py +455 -0
  109. vigil_mapper/parse_cache.py +795 -0
  110. vigil_mapper/refactor_boundary_builder.py +266 -0
  111. vigil_mapper/runtime_builder.py +527 -0
  112. vigil_mapper/runtime_tracer.py +243 -0
  113. vigil_mapper/runtime_tracer_entry.py +199 -0
  114. vigil_mapper/semantic_diff.py +71 -0
  115. vigil_mapper/source_adapters/__init__.py +109 -0
  116. vigil_mapper/source_adapters/_base.py +264 -0
  117. vigil_mapper/source_adapters/_ir.py +156 -0
  118. vigil_mapper/source_adapters/_lexer.py +309 -0
  119. vigil_mapper/source_adapters/_patterns.py +212 -0
  120. vigil_mapper/source_adapters/_treesitter.py +182 -0
  121. vigil_mapper/source_adapters/go.py +553 -0
  122. vigil_mapper/source_adapters/java.py +541 -0
  123. vigil_mapper/source_adapters/javascript.py +626 -0
  124. vigil_mapper/source_adapters/python.py +325 -0
  125. vigil_mapper/source_adapters/typescript.py +749 -0
  126. vigil_mapper/structural_builder.py +586 -0
  127. vigil_mcp/__init__.py +1 -0
  128. vigil_mcp/_jobs.py +587 -0
  129. vigil_mcp/_paths.py +93 -0
  130. vigil_mcp/forensic_server.py +419 -0
  131. vigil_mcp/map_server.py +452 -0
@@ -0,0 +1,240 @@
1
+ """Exception handling and boundary validation. Clusters 31, 32, 33."""
2
+ from __future__ import annotations
3
+
4
+ from .core import detect_language
5
+ from ...gate_models import (
6
+ EvidenceReference,
7
+ GateCategory,
8
+ GateFinding,
9
+ GateImpact,
10
+ GateSeverity,
11
+ RepairKind,
12
+ )
13
+ from ..common import build_finding
14
+ import logging
15
+ _log = logging.getLogger(__name__)
16
+
17
+
18
+ def _extract_except_body(lines: list[str], except_line: int) -> str:
19
+ if except_line + 1 >= len(lines):
20
+ return ""
21
+ except_indent = len(lines[except_line]) - len(lines[except_line].lstrip())
22
+ body_parts = []
23
+ for j in range(except_line + 1, min(except_line + 10, len(lines))):
24
+ line = lines[j]
25
+ if not line.strip():
26
+ continue
27
+ line_indent = len(line) - len(line.lstrip())
28
+ if line_indent <= except_indent:
29
+ break
30
+ body_parts.append(line.strip())
31
+ return "\n".join(body_parts)
32
+
33
+
34
+ def _is_swallowed(body: str) -> bool:
35
+ stripped = body.strip()
36
+ if not stripped or stripped == "pass":
37
+ return True
38
+ body_lines = [l.strip() for l in stripped.splitlines() if l.strip()]
39
+ if all(l in ("pass", "continue") for l in body_lines):
40
+ return True
41
+ if any(l.startswith("raise") for l in body_lines):
42
+ return False
43
+ if any(l.startswith("return ") for l in body_lines):
44
+ return False
45
+ log_only = all(
46
+ l.startswith(("log", "logger", "logging", "print(", "#", "warnings.warn")) # noqa: debug_print_scan # gate pattern reference, not a production print call
47
+ for l in body_lines
48
+ )
49
+ if log_only and len(body_lines) <= 2:
50
+ return False
51
+ return False
52
+
53
+
54
+ # Control-flow exceptions that are idiomatically caught-and-passed (NOT swallowed
55
+ # errors): broken pipe on closed stdout, clean Ctrl+C / shutdown, generator/async
56
+ # teardown, iteration end. Flagging `except KeyboardInterrupt: pass` as a swallowed
57
+ # error is a false positive.
58
+ _CONTROL_FLOW_EXCEPTIONS = frozenset({
59
+ "KeyboardInterrupt", "BrokenPipeError", "GeneratorExit", "SystemExit",
60
+ "StopIteration", "StopAsyncIteration", "CancelledError",
61
+ })
62
+
63
+
64
+ def assess_exception_swallowing(file_path: str, content: str) -> list[GateFinding]:
65
+ """Cluster 31: Detect swallowed exceptions."""
66
+ import re
67
+
68
+ if not content.strip():
69
+ return []
70
+ if detect_language(file_path) != "python":
71
+ return []
72
+ basename = file_path.replace("\\", "/").rsplit("/", 1)[-1] if "/" in file_path.replace("\\", "/") else file_path
73
+ if basename.startswith("test_") or basename.startswith("conftest"):
74
+ return []
75
+
76
+ lines = content.splitlines()
77
+ findings: list[GateFinding] = []
78
+ i = 0
79
+ while i < len(lines):
80
+ stripped = lines[i].strip()
81
+ if re.match(r'^except\s*:', stripped):
82
+ body = _extract_except_body(lines, i)
83
+ if _is_swallowed(body):
84
+ detail = f"Bare except with swallowed error (line {i + 1}): body is '{body.strip()[:60]}'"
85
+ findings.append(build_finding(
86
+ check_id="exception_swallow_scan",
87
+ category=GateCategory.RUNTIME_BEHAVIOR,
88
+ title=f"[exception_swallowing] {file_path}:{i + 1}",
89
+ severity=GateSeverity.HIGH,
90
+ impact=GateImpact.REVISE,
91
+ summary=detail,
92
+ recommendation="Use specific exception types and reraise or log+reraise.",
93
+ evidence=(EvidenceReference(kind="probe", path=file_path, detail=detail, ok=False),),
94
+ repair_kind=RepairKind.REPLACE_WITH_FAIL_LOUD.value,
95
+ executor_action=f"Fix swallowed exception at {file_path}:{i + 1}",
96
+ ))
97
+ elif re.match(r'^except\s+(Exception|BaseException)(\s+as\s+\w+)?\s*:', stripped):
98
+ body = _extract_except_body(lines, i)
99
+ if _is_swallowed(body):
100
+ exc_m = re.match(r'^except\s+(Exception|BaseException)', stripped)
101
+ exc_type = exc_m.group(1) if exc_m else "Exception"
102
+ detail = f"Broad `except {exc_type}` with swallowed error (line {i + 1})"
103
+ findings.append(build_finding(
104
+ check_id="exception_swallow_scan",
105
+ category=GateCategory.RUNTIME_BEHAVIOR,
106
+ title=f"[exception_swallowing] {file_path}:{i + 1}",
107
+ severity=GateSeverity.HIGH,
108
+ impact=GateImpact.REVISE,
109
+ summary=detail,
110
+ recommendation="Add reraise (raise) after logging, or use a more specific exception type.",
111
+ evidence=(EvidenceReference(kind="probe", path=file_path, detail=detail, ok=False),),
112
+ repair_kind=RepairKind.REPLACE_WITH_FAIL_LOUD.value,
113
+ executor_action=f"Fix broad except at {file_path}:{i + 1}",
114
+ ))
115
+ elif re.match(r'^except\s+\w', stripped):
116
+ body = _extract_except_body(lines, i)
117
+ exc_m = re.match(r'^except\s+([\w.]+)', stripped)
118
+ caught = exc_m.group(1).split(".")[-1] if exc_m else ""
119
+ if body.strip() == "pass" and caught not in _CONTROL_FLOW_EXCEPTIONS:
120
+ detail = f"Exception caught and silently passed (line {i + 1}): {stripped[:60]}"
121
+ findings.append(build_finding(
122
+ check_id="exception_swallow_scan",
123
+ category=GateCategory.RUNTIME_BEHAVIOR,
124
+ title=f"[exception_swallowing] {file_path}:{i + 1}",
125
+ severity=GateSeverity.MEDIUM,
126
+ impact=GateImpact.REVISE,
127
+ summary=detail,
128
+ recommendation="Log the exception and/or reraise it instead of passing silently.",
129
+ evidence=(EvidenceReference(kind="probe", path=file_path, detail=detail, ok=False),),
130
+ repair_kind=RepairKind.REPLACE_WITH_FAIL_LOUD.value,
131
+ executor_action=f"Fix silent pass at {file_path}:{i + 1}",
132
+ ))
133
+ i += 1
134
+ return findings[:10]
135
+
136
+
137
+ def assess_hardcoded_paths(file_path: str, content: str) -> list[GateFinding]:
138
+ """Cluster 32: Detect hardcoded absolute paths that break portability."""
139
+ import re
140
+
141
+ if not content.strip():
142
+ return []
143
+ lang = detect_language(file_path)
144
+ if lang not in ("python", "javascript", "typescript", "go", "rust", "java", "shell"):
145
+ return []
146
+ basename = file_path.replace("\\", "/").rsplit("/", 1)[-1] if "/" in file_path.replace("\\", "/") else file_path
147
+ if basename.startswith("test_") or basename.startswith("conftest"):
148
+ return []
149
+
150
+ path_patterns = [
151
+ (r'''["'][A-Z]:\\[^"']{3,}["']''', "Windows absolute path"),
152
+ (r'''["']/home/\w+[^"']*["']''', "Unix home directory path"),
153
+ (r'''["']/tmp/[^"']+["']''', "Hardcoded /tmp path (use tempfile)"),
154
+ (r'''["']/usr/(?:local/|bin/)[^"']+["']''', "Hardcoded /usr path"),
155
+ (r'''["']/var/(?:log|run|lib)/[^"']+["']''', "Hardcoded /var path"),
156
+ (r'''["']/etc/[^"']+["']''', "Hardcoded /etc config path"),
157
+ (r'''["']/opt/[^"']+["']''', "Hardcoded /opt path"),
158
+ (r'''["']/(?:root|srv|mnt)/[^"']+["']''', "Hardcoded system path"),
159
+ ]
160
+
161
+ findings: list[GateFinding] = []
162
+ for i, line in enumerate(content.splitlines(), 1):
163
+ stripped = line.strip()
164
+ if stripped.startswith("#") or stripped.startswith("//"):
165
+ continue
166
+ if stripped.startswith('"""') or stripped.startswith("'''"):
167
+ continue
168
+ if "http://" in line or "https://" in line or "ftp://" in line:
169
+ continue
170
+ if i == 1 and stripped.startswith("#!"):
171
+ continue
172
+ for pattern, description in path_patterns:
173
+ for m in re.finditer(pattern, line):
174
+ matched = m.group(0)
175
+ if "EXAMPLE" in stripped.upper() or "PLACEHOLDER" in stripped.upper():
176
+ continue
177
+ findings.append(build_finding(
178
+ check_id="hardcoded_path_scan",
179
+ category=GateCategory.CONTRACT,
180
+ title=f"[hardcoded_paths] {file_path}:{i}",
181
+ severity=GateSeverity.MEDIUM,
182
+ impact=GateImpact.REVISE,
183
+ summary=f"{description}: {matched[:80]}",
184
+ recommendation="Use os.path, pathlib.Path, or environment variables instead of hardcoded paths.",
185
+ evidence=(EvidenceReference(kind="probe", path=file_path, detail=f"{description}: {matched[:80]}", ok=False),),
186
+ repair_kind=RepairKind.FIX_CONTRACT.value,
187
+ executor_action=f"Replace hardcoded path at {file_path}:{i}",
188
+ ))
189
+ break
190
+ if len(findings) >= 10:
191
+ break
192
+ return findings
193
+
194
+
195
+ def assess_boundary_validation(file_path: str, content: str) -> list[GateFinding]:
196
+ """Cluster 33: Detect unvalidated external input at system boundaries."""
197
+ import re
198
+
199
+ if not content.strip():
200
+ return []
201
+ if detect_language(file_path) != "python":
202
+ return []
203
+ basename = file_path.replace("\\", "/").rsplit("/", 1)[-1] if "/" in file_path.replace("\\", "/") else file_path
204
+ if basename.startswith("test_") or basename.startswith("conftest"):
205
+ return []
206
+
207
+ findings: list[GateFinding] = []
208
+ for i, line in enumerate(content.splitlines(), 1):
209
+ stripped = line.strip()
210
+ if stripped.startswith("#"):
211
+ continue
212
+ detail: str | None = None
213
+ if re.search(r'os\.(system|popen)\s*\(\s*f["\']', stripped):
214
+ detail = f"Potential command injection: os.system/popen with f-string (line {i})"
215
+ elif re.search(r'subprocess\.\w+\s*\(.*shell\s*=\s*True', stripped) and ('f"' in stripped or "f'" in stripped):
216
+ detail = f"Potential command injection: subprocess with shell=True + f-string (line {i})"
217
+ elif re.search(r'\.(execute|executemany|query)\s*\(\s*f["\']', stripped):
218
+ if re.search(r'(?i)(SELECT|INSERT|UPDATE|DELETE|DROP|CREATE|ALTER)', stripped):
219
+ detail = f"Potential SQL injection: .execute() with f-string SQL (line {i})"
220
+ elif re.search(r'\.(execute|query)\s*\(\s*["\'].*%s.*["\'].*%\s*\(', stripped):
221
+ if re.search(r'(?i)(SELECT|INSERT|UPDATE|DELETE)', stripped):
222
+ detail = f"Potential SQL injection: .execute() with % formatting (line {i})"
223
+ elif re.search(r'\b(eval|exec)\s*\(\s*(?!["\'(])', stripped):
224
+ detail = f"Dangerous eval/exec with variable input (line {i})"
225
+ if detail:
226
+ findings.append(build_finding(
227
+ check_id="boundary_validation_scan",
228
+ category=GateCategory.TRUTH_BOUNDARY,
229
+ title=f"[boundary_validation] {file_path}:{i}",
230
+ severity=GateSeverity.HIGH,
231
+ impact=GateImpact.REVISE,
232
+ summary=detail,
233
+ recommendation="Validate and sanitize all external input before use in system calls.",
234
+ evidence=(EvidenceReference(kind="probe", path=file_path, detail=detail, ok=False),),
235
+ repair_kind=RepairKind.ADD_BOUNDARY_CHECK.value,
236
+ executor_action=f"Fix boundary validation at {file_path}:{i}",
237
+ ))
238
+ if len(findings) >= 10:
239
+ break
240
+ return findings