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.
- adapters/__init__.py +0 -0
- adapters/base.py +27 -0
- adapters/github_adapter.py +164 -0
- adapters/gitlab_adapter.py +207 -0
- adapters/local_adapter.py +136 -0
- banner.py +71 -0
- cli.py +606 -0
- config/__init__.py +1 -0
- config/rules.yaml +371 -0
- core/__init__.py +235 -0
- core/ast_detector.py +853 -0
- core/change.py +46 -0
- core/composer.py +93 -0
- core/evaluator.py +15 -0
- core/ignore_manager.py +71 -0
- core/knowledge.py +77 -0
- core/parser.py +181 -0
- core/parser_manager.py +104 -0
- core/quality_manager.py +117 -0
- core/renderer.py +197 -0
- core/rule_base.py +98 -0
- core/rule_runtime.py +103 -0
- core/rules.py +718 -0
- core/run_config.py +85 -0
- core/semantic_diff.py +359 -0
- core/signal_model.py +21 -0
- core/signals_registry.py +62 -0
- diffsense-2.2.12.dist-info/METADATA +18 -0
- diffsense-2.2.12.dist-info/RECORD +58 -0
- diffsense-2.2.12.dist-info/WHEEL +5 -0
- diffsense-2.2.12.dist-info/entry_points.txt +3 -0
- diffsense-2.2.12.dist-info/licenses/LICENSE +176 -0
- diffsense-2.2.12.dist-info/top_level.txt +11 -0
- diffsense_mcp/__init__.py +1 -0
- diffsense_mcp/launcher.py +28 -0
- diffsense_mcp/server.py +687 -0
- governance/lifecycle.py +54 -0
- main.py +318 -0
- rules/__init__.py +246 -0
- rules/api_compatibility.py +372 -0
- rules/collection_handling.py +349 -0
- rules/concurrency.py +194 -0
- rules/concurrency_adapter.py +250 -0
- rules/cross_language_adapter.py +444 -0
- rules/exception_handling.py +320 -0
- rules/go_rules.py +401 -0
- rules/null_safety.py +301 -0
- rules/resource_management.py +222 -0
- rules/yaml_adapter.py +195 -0
- run_audit.py +478 -0
- sdk/cpp_adapter.py +238 -0
- sdk/go_adapter.py +199 -0
- sdk/java_adapter.py +199 -0
- sdk/javascript_adapter.py +229 -0
- sdk/language_adapter.py +313 -0
- sdk/python_adapter.py +195 -0
- sdk/rule.py +63 -0
- 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()
|
sdk/language_adapter.py
ADDED
|
@@ -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())
|