diffsense 2.2.12__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 (58) hide show
  1. adapters/__init__.py +0 -0
  2. adapters/base.py +27 -0
  3. adapters/github_adapter.py +164 -0
  4. adapters/gitlab_adapter.py +207 -0
  5. adapters/local_adapter.py +136 -0
  6. banner.py +71 -0
  7. cli.py +606 -0
  8. config/__init__.py +1 -0
  9. config/rules.yaml +371 -0
  10. core/__init__.py +235 -0
  11. core/ast_detector.py +853 -0
  12. core/change.py +46 -0
  13. core/composer.py +93 -0
  14. core/evaluator.py +15 -0
  15. core/ignore_manager.py +71 -0
  16. core/knowledge.py +77 -0
  17. core/parser.py +181 -0
  18. core/parser_manager.py +104 -0
  19. core/quality_manager.py +117 -0
  20. core/renderer.py +197 -0
  21. core/rule_base.py +98 -0
  22. core/rule_runtime.py +103 -0
  23. core/rules.py +718 -0
  24. core/run_config.py +85 -0
  25. core/semantic_diff.py +359 -0
  26. core/signal_model.py +21 -0
  27. core/signals_registry.py +62 -0
  28. diffsense-2.2.12.dist-info/METADATA +18 -0
  29. diffsense-2.2.12.dist-info/RECORD +58 -0
  30. diffsense-2.2.12.dist-info/WHEEL +5 -0
  31. diffsense-2.2.12.dist-info/entry_points.txt +3 -0
  32. diffsense-2.2.12.dist-info/licenses/LICENSE +176 -0
  33. diffsense-2.2.12.dist-info/top_level.txt +11 -0
  34. diffsense_mcp/__init__.py +1 -0
  35. diffsense_mcp/launcher.py +28 -0
  36. diffsense_mcp/server.py +687 -0
  37. governance/lifecycle.py +54 -0
  38. main.py +318 -0
  39. rules/__init__.py +246 -0
  40. rules/api_compatibility.py +372 -0
  41. rules/collection_handling.py +349 -0
  42. rules/concurrency.py +194 -0
  43. rules/concurrency_adapter.py +250 -0
  44. rules/cross_language_adapter.py +444 -0
  45. rules/exception_handling.py +320 -0
  46. rules/go_rules.py +401 -0
  47. rules/null_safety.py +301 -0
  48. rules/resource_management.py +222 -0
  49. rules/yaml_adapter.py +195 -0
  50. run_audit.py +478 -0
  51. sdk/cpp_adapter.py +238 -0
  52. sdk/go_adapter.py +199 -0
  53. sdk/java_adapter.py +199 -0
  54. sdk/javascript_adapter.py +229 -0
  55. sdk/language_adapter.py +313 -0
  56. sdk/python_adapter.py +195 -0
  57. sdk/rule.py +63 -0
  58. sdk/signal.py +14 -0
@@ -0,0 +1,229 @@
1
+ """
2
+ JavaScript/TypeScript Language Adapter for DiffSense
3
+
4
+ Provides JavaScript-specific patterns and constructs for rule development.
5
+ """
6
+
7
+ import re
8
+ from typing import List, Set, Pattern, Dict
9
+ from .language_adapter import LanguageAdapter
10
+
11
+
12
+ class JavaScriptAdapter(LanguageAdapter):
13
+ """
14
+ Language adapter for JavaScript/TypeScript.
15
+
16
+ Provides JavaScript-specific patterns for concurrency, resource management,
17
+ error handling, and other language constructs.
18
+ """
19
+
20
+ def __init__(self):
21
+ super().__init__("javascript")
22
+ self._compile_patterns()
23
+
24
+ def _compile_patterns(self):
25
+ """Pre-compile commonly used patterns."""
26
+ # Thread Safety / Concurrency Patterns
27
+ self._lock_patterns = [
28
+ re.compile(r'new\s+Mutex\(\)'),
29
+ re.compile(r'await\s+mutex\.acquire\(\)'),
30
+ re.compile(r'\.lock\(\)'),
31
+ re.compile(r'with\s*\(\s*lock\s*\)'),
32
+ re.compile(r'synchronized\s*\('),
33
+ ]
34
+
35
+ self._unlock_patterns = [
36
+ re.compile(r'mutex\.release\(\)'),
37
+ re.compile(r'\.unlock\(\)'),
38
+ ]
39
+
40
+ self._volatile_patterns = [
41
+ re.compile(r'Atomic\w+\('),
42
+ re.compile(r'new\s+AtomicReference'),
43
+ ]
44
+
45
+ # Resource Management Patterns
46
+ self._resource_creation = [
47
+ re.compile(r'fs\.open\('),
48
+ re.compile(r'fs\.createReadStream\('),
49
+ re.compile(r'fs\.createWriteStream\('),
50
+ re.compile(r'database\.connect\('),
51
+ re.compile(r'new\s+Client\(.*\)\.connect\(\)'),
52
+ ]
53
+
54
+ # Error Handling Patterns
55
+ self._error_check = [
56
+ re.compile(r'catch\s*\([^)]*\)\s*\{'),
57
+ re.compile(r'if\s*\(\s*err\s*\)'),
58
+ re.compile(r'if\s*\(\s*error\s*\)'),
59
+ re.compile(r'if\s*\(\s*!\w+\s*\)\s*\{'),
60
+ re.compile(r'\.catch\(\s*\w*\s*=>'),
61
+ ]
62
+
63
+ self._error_ignore = [
64
+ re.compile(r'catch\s*\([^)]*\)\s*\{\s*\}'),
65
+ re.compile(r'catch\s*\(\s*\w+\s*\)\s*\{\s*\}'),
66
+ re.compile(r'\.catch\(\s*\(\s*\w*\s*\)\s*\{\s*\}\s*\)'),
67
+ ]
68
+
69
+ # Async/Promise Patterns
70
+ self._promise_handling = [
71
+ re.compile(r'\.then\('),
72
+ re.compile(r'\.catch\('),
73
+ re.compile(r'\.finally\('),
74
+ re.compile(r'await\s+'),
75
+ re.compile(r'async\s+function'),
76
+ ]
77
+
78
+ # Security Patterns
79
+ self._security = {
80
+ 'command_injection': [
81
+ re.compile(r'child_process\.exec\('),
82
+ re.compile(r'child_process\.execSync\('),
83
+ re.compile(r'eval\s*\('),
84
+ re.compile(r'new\s+Function\('),
85
+ re.compile(r'document\.write\('),
86
+ ],
87
+ 'hardcoded_secret': [
88
+ re.compile(r'password\s*[:=]\s*["\'][^"\']+["\']'),
89
+ re.compile(r'api[_-]?key\s*[:=]\s*["\'][^"\']+["\']'),
90
+ re.compile(r'secret[_-]?key\s*[:=]\s*["\'][^"\']+["\']'),
91
+ re.compile(r'access[_-]?token\s*[:=]\s*["\'][^"\']+["\']'),
92
+ re.compile(r'AKIA[0-9A-Z]{16}'),
93
+ ],
94
+ 'sql_injection': [
95
+ re.compile(r'query\s*\(\s*["\'].*\%.*["\']'),
96
+ re.compile(r'query\s*\(\s*[`].*\$\{'),
97
+ ],
98
+ 'deserialization': [
99
+ re.compile(r'JSON\.parse\('),
100
+ re.compile(r'yaml\.load\('),
101
+ re.compile(r'yaml\.unsafe_load\('),
102
+ ],
103
+ 'xss': [
104
+ re.compile(r'innerHTML\s*='),
105
+ re.compile(r'outerHTML\s*='),
106
+ re.compile(r'document\.write\('),
107
+ ],
108
+ }
109
+
110
+ # Console usage (should not be in production)
111
+ self._console_usage = [
112
+ re.compile(r'console\.log\('),
113
+ re.compile(r'console\.debug\('),
114
+ re.compile(r'console\.info\('),
115
+ re.compile(r'console\.warn\('),
116
+ re.compile(r'console\.error\('),
117
+ ]
118
+
119
+ # ==================== Thread Safety ====================
120
+
121
+ def get_thread_safe_types(self) -> Set[str]:
122
+ return {
123
+ 'Mutex', 'Lock', 'RLock', 'Semaphore',
124
+ 'AtomicReference', 'AtomicInteger', 'AtomicBoolean',
125
+ 'ConcurrentMap', 'ConcurrentHashMap',
126
+ 'Promise', 'AsyncIterable',
127
+ }
128
+
129
+ def get_unsafe_types(self) -> Set[str]:
130
+ return {
131
+ 'Object', 'Array', 'Map', 'Set',
132
+ 'string', 'number', 'boolean',
133
+ }
134
+
135
+ def get_lock_patterns(self) -> List[Pattern]:
136
+ return self._lock_patterns
137
+
138
+ def get_unlock_patterns(self) -> List[Pattern]:
139
+ return self._unlock_patterns
140
+
141
+ def get_volatile_patterns(self) -> List[Pattern]:
142
+ return self._volatile_patterns
143
+
144
+ # ==================== Resource Management ====================
145
+
146
+ def get_cleanup_keywords(self) -> Set[str]:
147
+ return {
148
+ 'close()', '.close()', 'finally', 'dispose()',
149
+ '.end()', '.destroy()', 'cleanup()', 'teardown()'
150
+ }
151
+
152
+ def get_resource_creation_patterns(self) -> List[Pattern]:
153
+ return self._resource_creation
154
+
155
+ # ==================== Error Handling ====================
156
+
157
+ def get_error_types(self) -> Set[str]:
158
+ return {
159
+ 'Error', 'TypeError', 'ReferenceError', 'SyntaxError',
160
+ 'RangeError', 'URIError', 'EvalError',
161
+ 'PromiseRejection', 'UnhandledPromiseRejection'
162
+ }
163
+
164
+ def get_error_check_patterns(self) -> List[Pattern]:
165
+ return self._error_check
166
+
167
+ def get_error_ignore_patterns(self) -> List[Pattern]:
168
+ return self._error_ignore
169
+
170
+ # ==================== Null Safety ====================
171
+
172
+ def get_null_checks(self) -> Set[str]:
173
+ return {
174
+ '=== null', '!== null', '== null', '!= null',
175
+ 'if (x)', 'if (!x)', 'if (x !== null)',
176
+ 'x &&', 'x?.', 'x ??', 'Optional.ofNullable'
177
+ }
178
+
179
+ def get_null_pointer_types(self) -> Set[str]:
180
+ return {
181
+ 'null', 'undefined', 'any', 'object'
182
+ }
183
+
184
+ # ==================== Concurrency Primitives ====================
185
+
186
+ def get_concurrency_primitives(self) -> Set[str]:
187
+ return {
188
+ 'Promise', 'async', 'await', 'setTimeout', 'setInterval',
189
+ 'Worker', 'WorkerThread', 'child_process',
190
+ 'Mutex', 'Semaphore', 'Atomics',
191
+ 'EventEmitter', 'EventTarget'
192
+ }
193
+
194
+ def get_thread_creation_patterns(self) -> List[Pattern]:
195
+ return self._promise_handling + [
196
+ re.compile(r'new\s+Worker\('),
197
+ re.compile(r'child_process\.spawn\('),
198
+ ]
199
+
200
+ # ==================== Security ====================
201
+
202
+ def get_dangerous_patterns(self) -> Dict[str, List[Pattern]]:
203
+ return self._security
204
+
205
+ # ==================== Additional JavaScript-specific ====================
206
+
207
+ def get_console_patterns(self) -> List[Pattern]:
208
+ """Get patterns for console usage (production issue)."""
209
+ return self._console_usage
210
+
211
+ def get_var_declaration_patterns(self) -> List[Pattern]:
212
+ """Get patterns for deprecated var declaration."""
213
+ return [
214
+ re.compile(r'\bvar\s+\w+'),
215
+ ]
216
+
217
+ def get_strict_comparison_patterns(self) -> List[Pattern]:
218
+ """Get patterns for loose equality (should use ===)."""
219
+ return [
220
+ re.compile(r'==\s*null'),
221
+ re.compile(r'!=\s*null'),
222
+ re.compile(r'==\s*undefined'),
223
+ re.compile(r'!=\s*undefined'),
224
+ ]
225
+
226
+
227
+ def get_adapter():
228
+ """Get JavaScript adapter instance."""
229
+ return JavaScriptAdapter()
@@ -0,0 +1,313 @@
1
+ """
2
+ Language Adapter System for DiffSense
3
+
4
+ This module provides language-agnostic abstractions for rule development.
5
+ The LanguageAdapter isolates language-specific patterns, allowing rules
6
+ to be written in a language-independent way.
7
+
8
+ Architecture:
9
+ - LanguageAdapter: Abstract base class defining the interface
10
+ - AdapterFactory: Factory to get the appropriate adapter for a language
11
+
12
+ Usage:
13
+ class MyConcurrencyRule(BaseRule):
14
+ def __init__(self, adapter: LanguageAdapter = None):
15
+ self._adapter = adapter or AdapterFactory.get_adapter("java")
16
+
17
+ def evaluate(self, diff_data, signals):
18
+ thread_safe_types = self._adapter.get_thread_safe_types()
19
+ # ... use language-specific patterns
20
+ """
21
+
22
+ from abc import ABC, abstractmethod
23
+ from typing import Dict, List, Optional, Set, Pattern, Any
24
+ import re
25
+
26
+
27
+ class LanguageAdapter(ABC):
28
+ """
29
+ Abstract base class for language-specific adapters.
30
+
31
+ This class defines the interface that all language adapters must implement.
32
+ It provides language-agnostic methods for accessing language-specific patterns
33
+ and constructs, allowing rules to be written in a language-independent way.
34
+ """
35
+
36
+ def __init__(self, language: str):
37
+ self._language = language
38
+ self._compiled_patterns: Dict[str, Pattern] = {}
39
+
40
+ @property
41
+ def language(self) -> str:
42
+ """Return the language identifier (e.g., 'java', 'go', 'python')"""
43
+ return self._language
44
+
45
+ # ==================== Thread Safety ====================
46
+
47
+ @abstractmethod
48
+ def get_thread_safe_types(self) -> Set[str]:
49
+ """
50
+ Return set of thread-safe collection/atomic types for this language.
51
+
52
+ Examples:
53
+ Java: {'ConcurrentHashMap', 'AtomicInteger', 'CopyOnWriteArrayList'}
54
+ Go: {'sync.Mutex', 'sync.RWMutex', 'chan', 'sync.Map'}
55
+ """
56
+ pass
57
+
58
+ @abstractmethod
59
+ def get_unsafe_types(self) -> Set[str]:
60
+ """
61
+ Return set of non-thread-safe types that could cause race conditions.
62
+
63
+ Examples:
64
+ Java: {'HashMap', 'ArrayList', 'HashSet'}
65
+ Go: {'map', 'slice'} (without synchronization)
66
+ """
67
+ pass
68
+
69
+ @abstractmethod
70
+ def get_lock_patterns(self) -> List[Pattern]:
71
+ """
72
+ Return regex patterns for lock/synchronization constructs.
73
+
74
+ Examples:
75
+ Java: ['synchronized', r'\\.lock\\(\\)', r'\\.wait\\(\\)']
76
+ Go: [r'\\.Lock\\(\\)', r'\\.RLock\\(\\)', r'sync\\.Mutex']
77
+ """
78
+ pass
79
+
80
+ @abstractmethod
81
+ def get_unlock_patterns(self) -> List[Pattern]:
82
+ """
83
+ Return regex patterns for unlock/release constructs.
84
+
85
+ Examples:
86
+ Java: [r'\\.unlock\\(\\)', r'\\.notify\\(\\)']
87
+ Go: [r'\\.Unlock\\(\\)', r'\\.RUnlock\\(\\)']
88
+ """
89
+ pass
90
+
91
+ @abstractmethod
92
+ def get_volatile_patterns(self) -> List[Pattern]:
93
+ r"""
94
+ Return regex patterns for volatile/atomic modifiers.
95
+
96
+ Examples:
97
+ Java: [r'volatile', r'AtomicInteger', r'AtomicLong']
98
+ Go: [r'atomic\.(Load|Store|Add|Swap|CompareAndSwap)']
99
+ """
100
+ pass
101
+
102
+ # ==================== Resource Management ====================
103
+
104
+ @abstractmethod
105
+ def get_cleanup_keywords(self) -> Set[str]:
106
+ """
107
+ Return keywords/patterns for resource cleanup.
108
+
109
+ Examples:
110
+ Java: {'close()', 'finally', 'try-with-resources'}
111
+ Go: {'defer', 'close()'}
112
+ """
113
+ pass
114
+
115
+ @abstractmethod
116
+ def get_resource_creation_patterns(self) -> List[Pattern]:
117
+ r"""
118
+ Return patterns for resource creation (to detect potential leaks).
119
+
120
+ Examples:
121
+ Java: [r'new\s+FileInputStream', r'new\s+Connection']
122
+ Go: [r'make\(', r'os\.Open']
123
+ """
124
+ pass
125
+
126
+ # ==================== Error Handling ====================
127
+
128
+ @abstractmethod
129
+ def get_error_types(self) -> Set[str]:
130
+ """
131
+ Return set of error/exception types for this language.
132
+
133
+ Examples:
134
+ Java: {'Exception', 'RuntimeException', 'Throwable'}
135
+ Go: {'error'}
136
+ """
137
+ pass
138
+
139
+ @abstractmethod
140
+ def get_error_check_patterns(self) -> List[Pattern]:
141
+ r"""
142
+ Return patterns for error checking.
143
+
144
+ Examples:
145
+ Java: [r'catch\s*\(\w+', r'if\s+\w+\s*==\s*null']
146
+ Go: [r'if\s+err\s*!=\s*nil', r'if\s+err\s*==\s*nil']
147
+ """
148
+ pass
149
+
150
+ @abstractmethod
151
+ def get_error_ignore_patterns(self) -> List[Pattern]:
152
+ r"""
153
+ Return patterns for ignoring/suppressing errors.
154
+
155
+ Examples:
156
+ Java: [r'catch\s*\(\s*Exception\s*\)', r'catch\s*\(\s*\)']
157
+ Go: [r'_\s*=\s*\w+\s*\(']
158
+ """
159
+ pass
160
+
161
+ # ==================== Null Safety ====================
162
+
163
+ @abstractmethod
164
+ def get_null_checks(self) -> Set[str]:
165
+ """
166
+ Return set of null check patterns for this language.
167
+
168
+ Examples:
169
+ Java: {'== null', '!= null', 'Objects.isNull', 'Optional.ofNullable'}
170
+ Go: {'== nil', '!= nil'}
171
+ """
172
+ pass
173
+
174
+ @abstractmethod
175
+ def get_null_pointer_types(self) -> Set[str]:
176
+ """
177
+ Return set of types that can cause null pointer errors.
178
+
179
+ Examples:
180
+ Java: {'Object', 'String', 'List', 'Map'}
181
+ Go: {'pointer', 'interface', 'slice', 'map', 'chan'}
182
+ """
183
+ pass
184
+
185
+ # ==================== Concurrency Primitives ====================
186
+
187
+ @abstractmethod
188
+ def get_concurrency_primitives(self) -> Set[str]:
189
+ """
190
+ Return set of language-specific concurrency primitives.
191
+
192
+ Examples:
193
+ Java: {'synchronized', 'volatile', 'Thread', 'ExecutorService'}
194
+ Go: {'go', 'chan', 'select', 'defer', 'sync.WaitGroup'}
195
+ """
196
+ pass
197
+
198
+ @abstractmethod
199
+ def get_thread_creation_patterns(self) -> List[Pattern]:
200
+ r"""
201
+ Return patterns for creating threads/goroutines.
202
+
203
+ Examples:
204
+ Java: [r'new\s+Thread', r'executor\.submit', r'CompletableFuture\.runAsync']
205
+ Go: [r'\bgo\s+\w+', r'go\s+func']
206
+ """
207
+ pass
208
+
209
+ # ==================== Security ====================
210
+
211
+ @abstractmethod
212
+ def get_dangerous_patterns(self) -> Dict[str, List[Pattern]]:
213
+ r"""
214
+ Return dictionary of security-related patterns.
215
+
216
+ Keys: category names (e.g., 'command_injection', 'sql_injection', 'hardcoded_secret')
217
+
218
+ Examples:
219
+ Java: {'sql_injection': [r'Statement.*\+', r'executeQuery.*\+']}
220
+ Go: {'command_injection': [r'exec\.Command', r'syscall\.Exec']}
221
+ """
222
+ pass
223
+
224
+ # ==================== Utility Methods ====================
225
+
226
+ def compile_pattern(self, pattern: str) -> Pattern:
227
+ """
228
+ Compile and cache a regex pattern.
229
+
230
+ Args:
231
+ pattern: Regular expression string
232
+
233
+ Returns:
234
+ Compiled regex pattern
235
+ """
236
+ if pattern not in self._compiled_patterns:
237
+ self._compiled_patterns[pattern] = re.compile(pattern)
238
+ return self._compiled_patterns[pattern]
239
+
240
+ def find_added_lines(self, raw_diff: str) -> List[str]:
241
+ """Extract added lines from diff."""
242
+ return [line for line in raw_diff.split('\n')
243
+ if line.startswith('+') and not line.startswith('+++')]
244
+
245
+ def find_removed_lines(self, raw_diff: str) -> List[str]:
246
+ """Extract removed lines from diff."""
247
+ return [line for line in raw_diff.split('\n')
248
+ if line.startswith('-') and not line.startswith('---')]
249
+
250
+ def has_pattern(self, text: str, patterns: List[Pattern]) -> bool:
251
+ """Check if text matches any of the given patterns."""
252
+ return any(p.search(text) for p in patterns)
253
+
254
+ def count_pattern(self, text: str, patterns: List[Pattern]) -> int:
255
+ """Count occurrences of patterns in text."""
256
+ return sum(len(p.findall(text)) for p in patterns)
257
+
258
+
259
+ class AdapterFactory:
260
+ """
261
+ Factory class for creating language adapters.
262
+
263
+ Usage:
264
+ java_adapter = AdapterFactory.get_adapter("java")
265
+ go_adapter = AdapterFactory.get_adapter("go")
266
+ """
267
+
268
+ _adapters: Dict[str, LanguageAdapter] = {}
269
+
270
+ @classmethod
271
+ def register_adapter(cls, language: str, adapter: LanguageAdapter):
272
+ """Register an adapter for a specific language."""
273
+ cls._adapters[language] = adapter
274
+
275
+ @classmethod
276
+ def get_adapter(cls, language: str) -> Optional[LanguageAdapter]:
277
+ """
278
+ Get an adapter for the specified language.
279
+
280
+ Args:
281
+ language: Language identifier ('java', 'go', 'python')
282
+
283
+ Returns:
284
+ LanguageAdapter instance, or None if not supported
285
+ """
286
+ language = language.lower()
287
+
288
+ if language in cls._adapters:
289
+ return cls._adapters[language]
290
+
291
+ # Try to load built-in adapters
292
+ if language == "java":
293
+ from .java_adapter import JavaAdapter
294
+ cls._adapters[language] = JavaAdapter()
295
+ elif language == "go":
296
+ from .go_adapter import GoAdapter
297
+ cls._adapters[language] = GoAdapter()
298
+ elif language == "python":
299
+ from .python_adapter import PythonAdapter
300
+ cls._adapters[language] = PythonAdapter()
301
+ elif language == "javascript":
302
+ from .javascript_adapter import JavaScriptAdapter
303
+ cls._adapters[language] = JavaScriptAdapter()
304
+ elif language == "cpp" or language == "c":
305
+ from .cpp_adapter import CppAdapter
306
+ cls._adapters[language] = CppAdapter()
307
+
308
+ return cls._adapters.get(language)
309
+
310
+ @classmethod
311
+ def get_supported_languages(cls) -> List[str]:
312
+ """Get list of supported languages."""
313
+ return list(cls._adapters.keys())